spring boot整合shiro
安全框架Shiro和Spring Security比较,本文主要围绕Shiro进行学习
一 Shiro 是一个强大而灵活的开源安全框架,能够清晰的处理认证 授权 管理会话以及,密码加密
01 .认证与授权相关概念
安全实体: 系统需要保护的具体对象数据
权限: 系统相关的功能操作,例如基本的CRUD
Authentication:身份认证授权/登录,验证用户是否拥有相应的身份
Authorization: 授权,即权限的认证,认证某个已认证的用户是否拥有某个权限
Session Manager : 会话管理.即用户登录后就是一次会话,在没有退出之前,所有信息都在会话中
Cryptography: 加密,保护数据的安全性
Web Support:web支持
Caching: 缓存
Concurrency: shiro支持多线程并发验证,一个线程中开启另一个线程,能把权限自动传播过去:
Remember Me:记住我
02. shiro四大核心,.如下是shiro架构
03 Shiro三个核心组件:Subject .SecurityManager和Realms
Subject: 主体,代表当前"用户",所有Subject都绑定到SecurityManager,是一个抽象的概念,与当前应用交互任何东西都是Subject,如网络爬虫,机器人,与Subject的所有交互都会委托给SecurityManager;
SecurityManager:安全管理器: 即所有与安全有关的操作都会与SecuityManager交互,管理所有的Subject;是Shiro的核心,它负责与后边介绍的其他组件进行交互
Realm: 域 , shiro从Realm获取安全数据(如 用户,角色,权限)SecurityManager要验证用户身份,那么他需要从Realm获取相应的用户,已确定使用户身份是否合法,也需要从Realm得到用户相应的角色/权限进行验证用于是否能进行操作,可以吧Realm看成DataSource.,即安全数据源
04 以下已springboot的一个项目中shiro运用为例
下面是pom.xml中相关jar
<!--shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!-- shiro ehcache -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>1.2.1</version>
</dependency>
ShiroConfig配置文件
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.prostate.common.config.Constant;
import com.prostate.common.redis.shiro.RedisCacheManager;
import com.prostate.common.redis.shiro.RedisManager;
import com.prostate.common.redis.shiro.RedisSessionDAO;
import com.prostate.system.shiro.UserRealm;
//import org.apache.shiro.cache.CacheManager;
import net.sf.ehcache.CacheManager;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.session.mgt.eis.MemorySessionDAO;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource; import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap; /**
* @author*/ @Configuration
public class ShiroConfig {
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.password}")
private String password;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.timeout}")
private int timeout; @Value("${spring.cache.type}")
private String cacheType; @Value("${server.session-timeout}")
private int tomcatTimeout; // @Autowired
// CacheManager cacheManager; @Bean
public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
} /**
* ShiroDialect,为了在thymeleaf里使用shiro的标签的bean
*
* @return
*/
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
// ShiroFilterFactoryBean 为了生成ShiroFilter,处理拦截资源文件问题
//它主要保持三项数据,securityManager,filters,fiterChainDefinition
// Shiro验证URL时,匹配成功就不在匹配,自上而下
//
@Bean
ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setLoginUrl("/login");
shiroFilterFactoryBean.setSuccessUrl("/index");
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/fonts/**", "anon");
filterChainDefinitionMap.put("/img/**", "anon");
filterChainDefinitionMap.put("/docs/**", "anon");
filterChainDefinitionMap.put("/druid/**", "anon");
filterChainDefinitionMap.put("/upload/**", "anon");
filterChainDefinitionMap.put("/files/**", "anon");
filterChainDefinitionMap.put("/logout", "logout");
filterChainDefinitionMap.put("/", "anon");
filterChainDefinitionMap.put("/blog", "anon");
filterChainDefinitionMap.put("/blog/open/**", "anon");
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
} @Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//设置realm.
securityManager.setRealm(userRealm());
// 自定义缓存实现 使用redis
if (Constant.CACHE_TYPE_REDIS.equals(cacheType)) {
securityManager.setCacheManager(cacheManager());
} else {
securityManager.setCacheManager(ehCacheManager());
}
securityManager.setSessionManager(sessionManager());
return securityManager;
} @Bean
UserRealm userRealm() {
UserRealm userRealm = new UserRealm();
return userRealm;
} /**
* 开启shiro aop注解支持.
* 使用代理方式;所以需要开启代码支持;
*
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
} /**
* 配置shiro redisManager
*
* @return
*/
@Bean
public RedisManager redisManager() {
RedisManager redisManager = new RedisManager();
redisManager.setHost(host);
redisManager.setPort(port);
redisManager.setExpire(1800);// 配置缓存过期时间
//redisManager.setTimeout(1800);
redisManager.setPassword(password);
return redisManager;
} /**
* cacheManager 缓存 redis实现
* 使用的是shiro-redis开源插件
*
* @return
*/
public RedisCacheManager cacheManager() {
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
return redisCacheManager;
} /**
* RedisSessionDAO shiro sessionDao层的实现 通过redis
* 使用的是shiro-redis开源插件
*/
@Bean
public RedisSessionDAO redisSessionDAO() {
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
redisSessionDAO.setRedisManager(redisManager());
return redisSessionDAO;
} @Bean
public SessionDAO sessionDAO() {
if (Constant.CACHE_TYPE_REDIS.equals(cacheType)) {
return redisSessionDAO();
} else {
return new MemorySessionDAO();
}
} /**
* shiro session的管理
*/
@Bean
public DefaultWebSessionManager sessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setGlobalSessionTimeout(tomcatTimeout * 1000);
sessionManager.setSessionDAO(sessionDAO());
Collection<SessionListener> listeners = new ArrayList<SessionListener>();
listeners.add(new BDSessionListener());
sessionManager.setSessionListeners(listeners);
return sessionManager;
} @Bean
public EhCacheManager ehCacheManager() {
EhCacheManager em = new EhCacheManager();
em.setCacheManager(CacheManager.create());
return em;
} }
这里补充一下ehcache和redis比较
ehcache直接在jvm虚拟机中缓存,速度快,效率高,但是缓存共享麻烦,集群分布式应用不方便
redis是通过socket访问缓存务,效率比ehcachedi,处理集群和分布式缓存方便,有成熟的方案
UserRealm
import java.util.HashMap;
import java.util.Map;
import java.util.Set; import com.prostate.common.config.ApplicationContextRegister;
import com.prostate.system.domain.UserToken;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired; import com.prostate.common.utils.ShiroUtils;
import com.prostate.system.dao.UserDao;
import com.prostate.system.domain.UserDO;
import com.prostate.system.service.MenuService; /**
* 自定义realm 安全的数据库
*/
public class UserRealm extends AuthorizingRealm {
/* @Autowired
UserDao userMapper;
@Autowired
MenuService menuService;*/ /**
*
* @param arg0
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
Long userId = ShiroUtils.getUserId();
MenuService menuService = ApplicationContextRegister.getBean(MenuService.class);
Set<String> perms = menuService.listPerms(userId);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setStringPermissions(perms);
return info;
} @Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String) token.getPrincipal();
Map<String, Object> map = new HashMap<>(16);
map.put("username", username);
String password = new String((char[]) token.getCredentials()); UserDao userMapper = ApplicationContextRegister.getBean(UserDao.class);
// 查询用户信息
UserDO user = userMapper.list(map).get(0); // 账号不存在
if (user == null) {
throw new UnknownAccountException("账号或密码不正确");
} // 密码错误
if (!password.equals(user.getPassword())) {
throw new IncorrectCredentialsException("账号或密码不正确");
} // 账号锁定
if (user.getStatus() == 0) {
throw new LockedAccountException("账号已被锁定,请联系管理员");
}
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());
return info;
} }
ShiroUtils
package com.prostate.common.utils; import com.prostate.system.domain.UserToken;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.subject.Subject; import com.prostate.system.domain.UserDO;
import org.springframework.beans.factory.annotation.Autowired; import java.lang.reflect.InvocationTargetException;
import java.security.Principal;
import java.util.Collection;
import java.util.List; public class ShiroUtils {
@Autowired
private static SessionDAO sessionDAO; public static Subject getSubjct() {
return SecurityUtils.getSubject();
}
public static UserDO getUser() {
Object object = getSubjct().getPrincipal();
return (UserDO)object;
}
public static Long getUserId() {
return getUser().getUserId();
}
public static void logout() {
getSubjct().logout();
} public static List<Principal> getPrinciples() {
List<Principal> principals = null;
Collection<Session> sessions = sessionDAO.getActiveSessions();
return principals;
}
}
Shiro 分析
①: Shiro常见的抛出异常
UnknownAccountException(账号不存在)
IncorrectCredentialsException(密码不存在)
DisabledAccountException(帐号被禁用)
LockedAccountException(帐号被锁定)
ExcessiveAttemptsException(登录失败次数过多)
ExpiredCredentialsException(凭证过期)等
②:Shiro标签
<shiro:authenticated> 登录之后
<shiro:notAuthenticated> 不在登录状态时
<shiro:guest> 用户在没有RememberMe时
<shiro:user> 用户在RememberMe时
<shiro:hasAnyRoles name="abc,123" > 在有abc或者123角色时
<shiro:hasRole name="abc"> 拥有角色abc
<shiro:lacksRole name="abc"> 没有角色abc
<shiro:hasPermission name="abc"> 拥有权限abc
<shiro:lacksPermission name="abc"> 没有权限abc
<shiro:principal> 显示用户登录名
③ Shiro过滤器链中的几种内置过滤器
--(1)认证过滤器
anon 匿名过滤器
authc 需要认证
authcBasic 表示httpBasic认证
user 表示必须存在用户,当登入操作时不做检查 --(2)授权过滤器
roles /admins/user/**=authc,roles[admin] (必须认证过,并拥有admin 角色)
perms
port
rest
ssl --(3) shiro 登录退出
shiro 内置的logout 过滤器(先配置logout,再在过滤器链中配置)
<bean id="logout" class="org.apache.shiro.web.filter.authc.LogoutFilter">
<property name="redirectUrl" value="/loginform" />
</bean>
filterChainDefinitions 配置 /logout = logout --(4) 关于登录过滤器的配置
通常将登录请求和使用的资源(js.css等)放开设置为anon,将其他的所有资源/** 设置为authc --(5)关于Session 失效的问题
每次请求都会经过过滤器链的过滤,对于authc的资源,如果登录失效,自动返回到默认登录首页 配置的loginUrl 中;
或者可以自定义个session 拦截的过滤器放入shiro 的过滤器链
二 Spring Security是一个能够为Spring的企业应用系统提供声明式安全访问控制解决方案的安全框架.它提供一组Spring应用上下文中配置Bean,充分利用Spring Ioc,DI,AOP,减少了为企业系统安全控制编写大量重复代码,它是一个轻量级的安全框架,他确保基于Spring的应用程序提供身份验证和授权支持,并配备了流行的安全算法实现捆绑在一起
后续详解 Spring Security
spring boot整合shiro的更多相关文章
- Spring Boot 整合 Shiro ,两种方式全总结!
在 Spring Boot 中做权限管理,一般来说,主流的方案是 Spring Security ,但是,仅仅从技术角度来说,也可以使用 Shiro. 今天松哥就来和大家聊聊 Spring Boot ...
- Spring Boot2 系列教程(三十二)Spring Boot 整合 Shiro
在 Spring Boot 中做权限管理,一般来说,主流的方案是 Spring Security ,但是,仅仅从技术角度来说,也可以使用 Shiro. 今天松哥就来和大家聊聊 Spring Boot ...
- spring boot整合shiro出现UnavailableSecurityManagerException
spring boot自带spring security,spring security自然不用说是一个强大的安全框架,但是用惯了shiro,一时半会用不来spring security,所以要在sp ...
- spring boot整合shiro后,部分注解(Cache缓存、Transaction事务等)失效的问题
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/elonpage/article/details/78965176 前言 整合有缓存.事务的sprin ...
- Spring boot整合shiro框架
ShiroConfiguration package com.energy.common.config; import java.util.LinkedHashMap; import java.uti ...
- Spring Boot 整合 Shiro实现认证及授权管理
Spring Boot Shiro 本示例要内容 基于RBAC,授权.认证 加密.解密 统一异常处理 redis session支持 介绍 Apache Shiro 是一个功能强大且易于使用的Java ...
- 上手spring boot项目(二)之spring boot整合shiro安全框架
题记:在学习了springboot和thymeleaf之后,想完成一个项目练练手,于是使用springboot+mybatis和thymeleaf完成一个博客系统,在完成的过程中出现的一些问题,将这些 ...
- spring boot 整合 shiro
shrio官网:https://shiro.apache.org/ Apache Shiro是一个功能强大且易于使用的Java安全框架,可执行身份验证,授权,加密和会话管理.借助Shiro易于理解的A ...
- Spring boot整合shiro权限管理
Apache Shiro功能框架: Shiro聚焦与应用程序安全领域的四大基石:认证.授权.会话管理和保密. #,认证,也叫作登录,用于验证用户是不是他自己所说的那个人: #,授权,也就是访问控制,比 ...
随机推荐
- PHP基本的语法结构
学过C语言的话,上手PHP语言就非常快了,如果你有bash shell的基础,那恭喜你,上手PHP会更快,我们先来了解一下一些比较简单的东西,界定符和注释在PHP中的写法: 一 php文档的语法结构 ...
- 设置UIButton中的文字和图片,设置UILabel的文在显示不同颜色
UIButton: UIEdgeInsets 在UIButton中有三个对EdgeInsets的设置:ContentEdgeInsets.titleEdgeInsets.imageEdgeInsets ...
- obj-c编程10:Foundation库中类的使用(6)[线程和操作队列]
任何语言都不能避而不谈线程这个东东,虽然他是和平台相关的鸟,虽说unix哲学比较讨厌线程的说...线程不是万能灵药,但有些场合还是需要的.谈到线程就不得不考虑同步和死锁问题,见如下代码: #impor ...
- LeetCode刷题之合并排序链表
合并两个有序链表并返回一个新的列表.新列表应该由连接在一起的节点前两个列表 给定实例:Input: 1->2->4, 1->3->4Output: 1->1->2- ...
- 合法的json数组字符串,转换json
List list = JSON.parseArray("[{'id':1,'name':'a'},{'id':2,'name','b'}]", JSONObject.class) ...
- 有关Java 锁原理
锁 锁是用来锁东西的,让别人打不开也看不到!在线程中,用这个“锁”隐喻来说明一个线程在“操作”一个目标(如一个变量)的时候,如果变量是被锁住的,那么其他线程就对这个目标既“操作”不了(挂起)也无法看到 ...
- Dubbo入门—搭建一个最简单的Demo框架
一.Dubbo背景和简介 1.电商系统的演进 Dubbo开始于电商系统,因此在这里先从电商系统的演变讲起. a.单一应用框架(ORM) 当网站流量很小时,只需一个应用,将所有功能如下单支付等都部署在一 ...
- Nodejs经验谈
前言 这里主要说一下之前使用Nodejs开发踩过的坑,只说坑不填坑,那就是赤裸地耍流氓,文中有大量的说明及填坑方法,了解的看官可以直接跳过. PS:说实话,Nodejs的坑确实蛮多的:但是上手简单,扩 ...
- Day4_名称空间与作用域
函数嵌套: 函数的嵌套调用:在调用一个函数的过程中,又调用了了另外一个函数 比如说比较多个值的大小,可以利用这种方法: def max2(x,y): if x > y: return x els ...
- 利用webmagic获取天猫评论
引言 爬取商品信息 爬取商品评论 数据清洗 1. 引言 现代网页往往其HTML只有基本结构,而数据是通过AJAX或其他方法获取后填充,这样的模式对爬虫有一定阻碍,但是熟练以后获取并不困难,本文以爬取天 ...