SpringBoot 集成 shiro安全框架
1、导入依赖
<!-- shiro安全框架依赖 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.2</version>
</dependency>
<!-- shiro+redis缓存插件 -->
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>3.1.0</version>
</dependency>
2、核心概念
- Shiro主要功能:身份认证
Authentication, 身份证认证,一般就是登录 - 授权:
Authorization, 给用户分配角色或者访问某些资源的权限 - 会话管理
Session Management,用户的会话管理员,多数情况下是web session - 加密
Cryptography, 数据加解密,比如密码加解密等
shiro关键字
- Subject (把操作交给SecurityManager):我们把用户或者程序称为主体(如用户,第三方服务,cron作业) ,主体去访问系统或者资源
- SecurityManager( 关联Realm):安全管理器,Subject的认证和授权都要在安全管理器下进行
- Realm(Shiro 连接数据的桥梁):数据域,Shiro和安全数据的连接器,好比jdbc连接数据库; 通过realm获取认证授权相关信息
- Authenticator:认证器,主要负责Subject的认证
- Authorizer:授权器,主要负责Subject的授权,控制subject拥有的角色或者权限
- Cryptography:加解密,Shiro的包含易于使用和理解的数据加解密方法,简化了很多复杂的api
- Cache Manager:缓存管理器,比如认证或授权信息,通过缓存进行管理,提高性能
用户的登录流程
3、shiro验证类方法
//用户是否登陆(验证)成功,验证成功返回true
subject.isAuthenticated()
//检查用户是否有对应的角色
subject .hasRole("root")
//获取subject名
subject. getprincipal()
//检查是否有对应的角色,无返回值,直接在SecurityManager 里面进行判断
subject . checkRole( " admin" )
/ /退出登录
subject. logout();
4、Session管理
5、shiro内置过滤器
简介:内置过滤器 => 身份验证
- 核心过滤器类: DefaultFilter, 配置哪个路径对应哪个拦截器进行处理
- authc: org.apache.shiro.web.filter.authc.FormAuthenticationFilter
。需要认证登录才能访问 - user: org.apache.shiro.web.filter.authc.UserFilter
。用户拦截器,表示必须存在用户。 - anon: org.apache.shiro.web.filter.authc.AnonymousFilter
*匿名拦截器,不需要登录即可访问的资源,匿名用户或游客,- -般用于过滤静态资源。 - roles: org.apache.shiro.web.filter .authz.RolesAuthorizationFilter
。角色授权拦截器,验证用户是或否拥有角色。
。参数可写多个,表示某些角色才能通过,多个参数时写roles[“admin,user”], 当有多个参数时必须每个
参数都通过才算通过 - perms: org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
。权限授权拦截器,验证用户是否拥有权限
。参数可写多个,表示需要某些权限才能通过,多个参数时写perms[“user, admin”], 当有多个参数时必须每个参数都通过才算可以
6、实例—前后端分离
自定义Realm
●步骤:
。创建一个类,继承AuthorizingRealm->AuthenticatingRealm->CachingRealm->Realm
。重写授权方法doGetAuthorizationInfo
。重写认证方法doGetAuthenticationInfo
●方法:
。当用户登陆的时候会调用doGetAuthenticationInfo
。进行权限校验的时候会调用: doGetAuthorizationInfo
●对象介绍
。UsernamePasswordToken :对应就 是shiro的token中有Principal和Credential
UsernamePasswordToken-》HostAuthenticationToken-》AuthenticationToken
。SimpleAuthorizationInfo: 代表用户角色权限信息
。SimpleAuthenticationInfo :代表该用户的认证信息
public class CustomRealm extends AuthorizingRealm {
@Autowired
TeacherDAO teaDao;
// 用户授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 为用户授权
// (1) 拿到当前登录的用户
Subject subject = SecurityUtils.getSubject();
Teacher tea = (Teacher) subject.getPrincipal();
// (2) 给用户授权,根据他的字段中有的权限进行授权
info.addStringPermission(tea.getTeaSecurity());
System.out.println("正在授权");
return info;
}
// 用户认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 获取封装的用户信息
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
// 验证用户名是否正确
Teacher tea = teaDao.findByTeaUser(userToken.getUsername());
if(tea==null){
return null;
}
// 框架验证密码是否正确 ,注意:传递的第一个参数,在用户授权的doGetAuthorizationInfo方法中可以拿到,否则拿不到
System.out.println(this.getClass().getName());
return new SimpleAuthenticationInfo(tea,teaDao.findByTeaUser(userToken.getUsername()).getTeaPass(),this.getClass().getName());
}
}
7、自定义SessionManager管理器
public class CustomSessionManager extends DefaultWebSessionManager {
private static final String AUTHORIZATION = "token";
private static final Logger log = LoggerFactory.getLogger(DefaultWebSessionManager.class);
public CustomSessionManager() {
super();
}
/***
* 获取sessionid
*/
@Override
protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
String sessionid = WebUtils.toHttp(request).getHeader(AUTHORIZATION);
if (sessionid != null) {
System.out.println(111111);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,
ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE);
// 判断sessionid是否有效
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sessionid);
// 表示sessionid为有效
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
return sessionid;
} else {
System.out.println(22222);
return super.getSessionId(request, response);
}
}
}
搭建shiro环境
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
// 核心类: ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager")
DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
// 关联DefaultWebSecurityManager对象,设置安全管理器
bean.setSecurityManager(securityManager);
// *********************** 检查是否非法访问 ****************************
// bean.setLoginUrl("/tea/toLogin"); // (1)检测没有登录时(非法访问),跳转的url,,
// bean.setSuccessUrl("/tea/index"); // (2)登陆成功后,跳转的url,
// bean.setUnauthorizedUrl("/tea/yz"); // (3)认证不通过跳转,先验证登陆,再验证是否有权限
/***
* 添加shiro的内置过滤器
* anon: 无需认证就可以访问, 游客状态
* authc: 必须认证了才能访问, 登陆状态
* user: 必须有 “记住我” 功能才能用
* perms: 拥有某个资源的权限才能访问, 资源状态 perms[user:add]
* role: 拥有某个角色权限才能访问 角色状态 role[mldn,admin]
* 注意:过滤连是顺序执行,从上到下,一般/** 发到最下面
*/
// 必须使用LinkedHashMap对象,表示有序的
Map<String, String> filterMap = new LinkedHashMap<>();
// *********************** 为URL路径设置访问权限****************************
filterMap.put("/upload/**", "anon");
filterMap.put("/static/**", "anon");
filterMap.put("/tea/getAll", "authc");
// ***********************************************************************
// 将添加的验证规则添加到安全管理器中
bean.setFilterChainDefinitionMap(filterMap);
return bean;
}
// 核心类: DefaultWebSecurityManager
@Bean(name = "defaultWebSecurityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") CustomRealm customRealm,
@Qualifier("sessionManager") SessionManager sessionManager,
@Qualifier("redisCacheManager") RedisCacheManager redisCacheManager) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 前后端分离必须设置下面的sessionManager,使用自定义的session管理类
securityManager.setSessionManager(sessionManager);
// 关联自定义类UserRealm
securityManager.setRealm(customRealm);
// 启用缓存,使用自定义cacheManager,在使用redis缓存时,必须保证启动了redis服务
// securityManager.setCacheManager(redisCacheManager);
return securityManager;
}
// 核心类: 创建realm对象,需要自定义类,并添加进容器中
@Bean
public CustomRealm userRealm() {
CustomRealm customRealm = new CustomRealm();
// userRealm.setCredentialsMatcher(); 密码加密
return customRealm;
}
// 自定义SessionId,用于前后端分离
@Bean
public SessionManager sessionManager(@Qualifier("redisSessionDAO") RedisSessionDAO redisSessionDAO) {
CustomSessionManager manager = new CustomSessionManager();
// sessionId超时时间,默认 30分钟,
// manager.setGlobalSessionTimeout(3000000);
// 配置session持久化,有可能subject.getPrincipal();在强转为vo对象时失败
// manager.setSessionDAO(redisSessionDAO);
return manager;
}
// 密码加密
// @Bean
// public HashedCredentialsMatcher hashedCredentialsMatcher(){
// HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
// hashedCredentialsMatcher.setHashAlgorithmName("md5");
//// 散列两次,相当于两次加密
// hashedCredentialsMatcher.setHashIterations(2);
// }
// ******************************************以下需要依赖redis服务************************************************
// 配置redisManger
@Bean(name = "RedisManager")
public RedisManager getRedisManager() {
RedisManager manager = new RedisManager();
manager.setHost("localhost");
// 设置Redis的默认端口
manager.setPort(6379);
return manager;
}
/***
* 配置cache具体实现类
* @return
*/
@Bean
public RedisCacheManager redisCacheManager(@Qualifier("RedisManager") RedisManager redisManager) {
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager);
// 设置过期时间、单位是秒,20s
redisCacheManager.setExpire(20);
return redisCacheManager;
}
/***
* 自定义session持久化
* @return
*/
@Bean(name = "redisSessionDAO")
public RedisSessionDAO redisSessionDAO(@Qualifier("RedisManager") RedisManager redisManager) {
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
redisSessionDAO.setRedisManager(redisManager);
return redisSessionDAO;
}
}
注解方式
@RequiresRoles(value={"admin", "editor"}, logical= Logical.AND)
。需要角色admin和editor两个角色AND表示两个同时成立
@RequiresPermissions (value={"user:add", "user:del"}, logical= Logical.OR)
。需要权限user:add或user:del权限其中-个,OR是或的意思。
@RequiresAuthentication
。已经授过权(已经登录过),调用Subject.isAuthenticated()返回true
@RequifesUser
。身份验证或者通过记住我登录的