本文共 30677 字,大约阅读时间需要 102 分钟。
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws Exception { HttpServletRequest httpRequest=(HttpServletRequest)request; HttpServletResponse httpResponse=(HttpServletResponse)response; HttpSession session=httpRequest.getSession(); if(session.getAttribute("username")!=null){ chain.doFilter(request, response); } else { httpResponse.sendRedirect(httpRequest.getContextPath()+"/login.jsp"); } }
什么是ACL和RBAC
简介:介绍主流的权限框架 Apache Shiro、spring Security
什么是 spring Security:官网基础介绍
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。
一句话:Spring Security 的前身是 Acegi Security ,是 Spring 项目组中用来提供安全认证服务的框架
什么是 Apache Shiro:官网基础介绍
https://github.com/apache/shiro
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
一句话:Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能
两个优缺点,应该怎么选择
Apache Shiro比Spring Security , 前者使用更简单
Shiro 功能强大、 简单、灵活, 不跟任何的框架或者容器绑定,可以独立运行
Spring Security 对Spring 体系支持比较好,脱离Spring体系则很难开发
SpringSecutiry 支持Oauth鉴权 https://spring.io/projects/spring-security-oauth,Shiro需要自己实现
总结:两个框架没有谁超过谁,大体功能一致,新手一般先推荐Shiro,学习会容易点
直达Apache Shiro官网 http://shiro.apache.org/introduction.html
http://shiro.apache.org/architecture.html
Authentication,身份证认证,一般就是登录
Authorization,给用户分配角色或者访问某些资源的权限
Session Management, 用户的会话管理员,多数情况下是web session
Cryptography, 数据加解密,比如密码加解密等
subject:正如我们在刚才提到,在Subject
本质上是当前正在执行的用户的安全特定“视图”。“用户”一词通常表示一个人,一个人Subject
可以是一个人,但它也可以表示第三方服务,守护程序帐户,cron作业或类似的东西-基本上是当前与该软件交互的任何东西。
Subject
实例都绑定到(并要求)SecurityManager
。当您与互动时Subject
,这些互动会转化为与主题相关的互动SecurityManager
。
SecurityManager:这SecurityManager
是Shiro体系结构的核心,并充当一种“伞”对象,该对象协调其内部安全组件,这些安全组件一起形成对象图。但是,一旦为应用程序配置了SecurityManager及其内部对象图,通常就不用理会它了,应用程序开发人员几乎所有的时间都花在Subject
API上。
稍后我们将SecurityManager
详细讨论,但是重要的是要认识到,当您与进行交互时Subject
,SecurityManager
对于任何Subject
安全操作而言,确实是幕后工作。这反映在上面的基本流程图中。
realm:领域充当Shiro与应用程序的安全数据之间的“桥梁”或“连接器”。当真正需要与安全性相关的数据(例如用户帐户)进行交互以执行身份验证(登录)和授权(访问控制)时,Shiro会从一个或多个为应用程序配置的领域中查找许多此类内容。
从这个意义上说,领域本质上是特定于安全性的:它封装了数据源的连接详细信息,并根据需要使关联数据可用于Shiro。在配置Shiro时,您必须至少指定一个领域用于身份验证和/或授权。所述SecurityManager
可与多个境界被配置,但至少有一个是必需的。
Shiro提供了开箱即用的领域,可以连接到许多安全数据源(又名目录),例如LDAP,关系数据库(JDBC),文本配置源(例如INI和属性文件)等。如果默认的Realms不能满足您的需求,那么您可以插入自己的Realm实现以表示自定义数据源。
像其他内部组件一样,ShiroSecurityManager
管理着如何使用领域来获取要表示为Subject
实例的安全性和身份数据。
引入依赖:
org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test org.apache.shiro shiro-spring 1.4.0 mysql mysql-connector-java
user:
role:
permission:
user_role:
role_permission:
//是否有对应的角色subject.hasRole("root")//获取subject名subject.getPrincipal()//检查是否有对应的角色,无返回值,直接在SecurityManager里面进行判断subject.checkRole("admin")//检查是否有对应的角色subject.hasRole("admin")//退出登录subject.logout();
shiro测试认证测试:
/** * @author zb * @date 2021/1/31 13:44 * @Description: @Before @Test @After @AfterClass shiro授权与认证 简单测试 */public class ShiroApplicationTest { private SimpleAccountRealm accountRealm = new SimpleAccountRealm(); private DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); @Before public void init(){ // 初始化数据源 accountRealm.addAccount("zhangsan","123","root","admin"); accountRealm.addAccount("lisi","456","user"); // 构建环境 defaultSecurityManager.setRealm(accountRealm); } @Test public void testAuthentication(){ SecurityUtils.setSecurityManager(defaultSecurityManager); // 获取当前主体 Subject subject = SecurityUtils.getSubject(); // 输入账号和密码 UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("zhangsan", "123"); // 登录认证 subject.login(usernamePasswordToken); System.out.println("subject.isAuthenticated() = " + subject.isAuthenticated()); // 是否有对应的角色 boolean hasRole = subject.hasRole("root"); System.out.println("hasRole = " + hasRole); // 登录名 Object principal = subject.getPrincipal(); System.out.println("principal.toString() = " + principal.toString()); // 退出登录 subject.logout(); System.out.println("subject.isAuthenticated() = " + subject.isAuthenticated());}
shiro默认自带的realm和常见使用方法
apache Shiro 自定义Realm实战
步骤:
方法:
对象介绍
UsernamePasswordToken : 对应就是 shiro的token中有Principal和Credential
UsernamePasswordToken-》HostAuthenticationToken-》AuthenticationToken
SimpleAuthorizationInfo:代表用户角色权限信息
SimpleAuthenticationInfo :代表该用户的认证信息
public class CustomRealm extends AuthorizingRealm { @Autowired private UserService userService; /** * 用户校验 【权限】 的时候会调用该方法 * @param principalCollection * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>> 进入到授权"); // 获取到身份信息 Object primaryPrincipal = principalCollection.getPrimaryPrincipal(); User newUser = (User) primaryPrincipal; UserVo userVo = userService.findAllUserInfoByUsername(newUser.getUsername()); ListroleList = userVo.getRoleList(); // 从数据库中获取到[权限] Set permissions = new HashSet<>(); // 从数据获取【角色】 Set roles = new HashSet<>(roleList.size()); roleList.forEach(roleVo -> { List permissionList = roleVo.getPermissionList(); String roleName = roleVo.getName(); roles.add(roleName); permissionList.forEach(permission -> { String name = permission.getName(); permissions.add(name); }); }); SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); simpleAuthorizationInfo.setStringPermissions(permissions); simpleAuthorizationInfo.setRoles(roles); return simpleAuthorizationInfo; } /** * 用户登录 的时候回调用此方法 * @param authenticationToken * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 进入到认证"); // 身份信息 Object principal = authenticationToken.getPrincipal(); String username = (String) principal; // 从数据库获取密码 User user = userService.findUserInfoByUsername(username);// UserVo userVo = userService.findAllUserInfoByUsername(username); // 获取到密码 String password = user.getPassword(); if (StrUtil.isEmpty(password)){ return null; } SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user,password, this.getName()); return simpleAuthenticationInfo; }}
配置:
@Bean public CustomRealm customRealm(){ CustomRealm customRealm = new CustomRealm(); // 使用加密方式验证 登录[设置加密方式] customRealm.setCredentialsMatcher(hashedCredentialsMatcher()); return customRealm; }
Shiro认证和授权流程的源码,和断点测试
认证流程:subject.login(usernamePasswordToken); DelegatingSubject->login() DefaultSecurityManager->login() AuthenticatingSecurityManager->authenticate() AbstractAuthenticator->authenticate() ModularRealmAuthenticator->doAuthenticate() ModularRealmAuthenticator->doSingleRealmAuthentication() AuthenticatingRealm->getAuthenticationInfo() 补充:密码验证方法 AuthenticatingRealm-> assertCredentialsMatch()授权流程:subject.checkRole("admin") DelegatingSubject->checkRole() AuthorizingSecurityManager->checkRole() ModularRealmAuthorizer->checkRole() AuthorizingRealm->hasRole() AuthorizingRealm->doGetAuthorizationInfo()
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
perms:org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
authcBasic:org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
logout:org.apache.shiro.web.filter.authc.LogoutFilter
shiroFilterFactoryBean.setLoginUrl();
设置的 urlport:org.apache.shiro.web.filter.authz.PortFilter
ssl:org.apache.shiro.web.filter.authz.SslFilter
URL权限采取第一次匹配优先的方式? : 匹配一个字符,如 /user? , 匹配 /user3,但不匹配/user/;* : 匹配零个或多个字符串,如 /add* ,匹配 /addtest,但不匹配 /user/1** : 匹配路径中的零个或多个路径,如 /user/** 将匹 配 /user/xxx 或 /user/xxx/yyy例子/user/**=filter1/user/add=filter2请求 /user/add 命中的是filter1拦截器
/** * @author zb * @date 2021/2/11 15:00 * @Description: * ("/admin/**", "roles[admin,root]"); 自定义过滤器,满足当用户拥有 admin或root 即可访问 /admin */public class CustomRolesOrAuthorizationFilter extends AuthorizationFilter { /** * {@link org.apache.shiro.web.filter.mgt.DefaultFilter} * {@link RolesAuthorizationFilter} * @param request * @param response * @param mappedValue * @return * @throws Exception */ @Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { // 获取当前登录用户 Subject subject = getSubject(request, response); // 获取配置的 角色集合 String[] rolesArray = (String[]) mappedValue; if (rolesArray == null || rolesArray.length == 0) { // 没有角色限制,则就可以直接访问 return true; } Setroles = CollectionUtils.asSet(rolesArray); // 若有其中一个角色即可访问 for (int i = 0; i < rolesArray.length; i++) { if (subject.hasRole(rolesArray[i])) { return true; } } return false; }}
配置:
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 自定义过滤器 MapfiltersMap = new LinkedHashMap<>(); filtersMap.put("roleOrFilter",new CustomRolesOrAuthorizationFilter()); // shiroFilterFactoryBean绑定自定义过滤器 shiroFilterFactoryBean.setFilters(filtersMap);
数据安全核心知识,介绍常见的处理办法,Shiro 里的 CredentialsMatcher使用
/** * 设置密码 加密相关 * @return */ @Bean public HashedCredentialsMatcher hashedCredentialsMatcher(){ HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); // 设置加密方式 hashedCredentialsMatcher.setHashAlgorithmName("md5"); // 设置hash次数 hashedCredentialsMatcher.setHashIterations(2); return hashedCredentialsMatcher; }
/** * 加密测试 * @param args */ public static void main(String[] args) { String hashName = "md5"; String password = "123456"; // 加密方式 加密密码 盐 hash次数 SimpleHash simpleHash = new SimpleHash(hashName, password, null, 2); System.out.println("simpleHash = " + simpleHash); }
讲解权限角色控制 @RequiresRoles, @RequiresPermissions等注解的使用和编程式控制
配置文件的方式
@Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); // 需要登录的接口,如果访问某个接口,【需要登录却没登录】,则调用此接口(如果不是前后端分离,则跳转页面) shiroFilterFactoryBean.setLoginUrl("/pub/need_login"); // 登录成功,跳转Url,如果前后端分离,则没这个调用 shiroFilterFactoryBean.setSuccessUrl("/"); // 没有权限,未授权就会调用此方法,先验证登录--》 再验证是否有权限 shiroFilterFactoryBean.setUnauthorizedUrl("/pub/not_permit"); // 自定义过滤器 MapfiltersMap = new LinkedHashMap<>(); filtersMap.put("roleOrFilter",new CustomRolesOrAuthorizationFilter()); // shiroFilterFactoryBean绑定自定义过滤器 shiroFilterFactoryBean.setFilters(filtersMap); // 默认过滤器 【org.apache.shiro.web.filter.mgt DefaultFilter】 // 拦截器路径,部门路径无法进行拦截,时有时无;是因为同学使用的hashmap,无序的,应该改为LinkHashMap Map filterChainDefinitionMap = new LinkedHashMap<>(); // 退出过滤器 filterChainDefinitionMap.put("/logout", "logout"); // 匿名可以访问,也就是游客模式 filterChainDefinitionMap.put("/pub/**", "anon"); // 需要登录才可以访问 filterChainDefinitionMap.put("/authc/**", "authc"); // 需要管理员 角色才可以访问 此处配置意思为:用户同时拥有 admin和root 权限才可以访问 filterChainDefinitionMap.put("/admin/**", "roles[admin,root]"); // 有编辑权限才可以访问 filterChainDefinitionMap.put("/video/update", "perms[video_update]"); // 自定义过滤器 filterChainDefinitionMap.put("/admin/**","roleOrFilter[admin,root]"); // 过滤链是顺序执行,从上而下,一般讲 /** 放到最下面 // authc : url定义必须通过认证才可以访问 // anon : url可以匿名访问 filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; }
注解方式
@RequiresRoles(value={“admin”, “editor”}, logical= Logical.AND)
@RequiresPermissions (value={“user:add”, “user:del”}, logical= Logical.OR)
@RequiresAuthentication
@RequiresUser
shiro缓存
shiro中提供了对认证信息和授权信息的缓存。
默认是关闭认证信息缓存的,对于授权信息的缓存shiro默认开启的(因为授权的数据量大
AuthenticatingRealm 及 AuthorizingRealm 分别提供了对AuthenticationInfo 和 AuthorizationInfo 信息的缓存。
Shiro Session模块作用和SessionManager
什么是会话session
什么是会话管理器SessionManager
会话管理器管理所有subject的所有操作,是shiro的核心组件
核心方法:
//开启一个sessionSession start(SessionContext context);//指定Key获取sessionSession getSession(SessionKey key)
shiro中的会话管理器有多个实现
SessionDao 会话存储/持久化
SessionDAO AbstractSessionDAO CachingSessionDAO EnterpriseCacheSessionDAO MemorySessionDAO
核心方法
//创建Serializable create(Session session);//获取Session readSession(Serializable sessionId) throws UnknownSessionException;//更新void update(Session session) //删除,会话过期时会调用void delete(Session session);//获取活跃的sessionCollectiongetActiveSessions();
会话存储有多个实现
附属资料:
RememberMe 1、 Cookie 写到客户端并 保存 2、 通过调用subject.login()前,设置 token.setRememberMe(true); 3、 关闭浏览器再重新打开;会发现浏览器还是记住你的 4、 注意点: - subject.isAuthenticated() 表示用户进行了身份验证登录的,即Subject.login 进行了登录 - subject.isRemembered() 表示用户是通过RememberMe登录的 - subject.isAuthenticated()==true,则 subject.isRemembered()==false, 两个互斥 - 总结:特殊页面或者API调用才需要authc进行验证拦截,该拦截器会判断用户是否是通过 subject.login()登录,安全性更高,其他非核心接口或者页面则通过user拦截器处理即可
shiro整合springboot配置文件
package cn.mesmile.shiro.config;import org.apache.shiro.authc.credential.HashedCredentialsMatcher;import org.apache.shiro.mgt.SecurityManager;import org.apache.shiro.session.mgt.SessionManager;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.crazycake.shiro.RedisCacheManager;import org.crazycake.shiro.RedisManager;import org.crazycake.shiro.RedisSessionDAO;import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.DependsOn;import javax.servlet.Filter;import java.util.LinkedHashMap;import java.util.Map;/** * @author zb * @date 2021/2/7 17:58 * @Description: */@Configurationpublic class MyShiroConfig { /** * 配置流程和思路 * * shiroFilterFactoryBean-》 * SecurityManager-》 * CustomSessionManager * CustomRealm-》hashedCredentialsMatcher * SessionManager * * DefaultSessionManager: 默认实现,常用于javase * ServletContainerSessionManager: web环境 * DefaultWebSessionManager:常用于自定义实现 */ @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); // 需要登录的接口,如果访问某个接口,【需要登录却没登录】,则调用此接口(如果不是前后端分离,则跳转页面) shiroFilterFactoryBean.setLoginUrl("/pub/need_login"); // 登录成功,跳转Url,如果前后端分离,则没这个调用 shiroFilterFactoryBean.setSuccessUrl("/"); // 没有权限,未授权就会调用此方法,先验证登录--》 再验证是否有权限 shiroFilterFactoryBean.setUnauthorizedUrl("/pub/not_permit"); // 自定义过滤器 MapfiltersMap = new LinkedHashMap<>(); filtersMap.put("roleOrFilter",new CustomRolesOrAuthorizationFilter()); // shiroFilterFactoryBean绑定自定义过滤器 shiroFilterFactoryBean.setFilters(filtersMap); // 默认过滤器 【org.apache.shiro.web.filter.mgt DefaultFilter】 // 拦截器路径,部门路径无法进行拦截,时有时无;是因为同学使用的hashmap,无序的,应该改为LinkHashMap Map filterChainDefinitionMap = new LinkedHashMap<>(); // 退出过滤器 filterChainDefinitionMap.put("/logout", "logout"); // 匿名可以访问,也就是游客模式 filterChainDefinitionMap.put("/pub/**", "anon"); // 需要登录才可以访问 filterChainDefinitionMap.put("/authc/**", "authc"); // 需要管理员 角色才可以访问 此处配置意思为:用户同时拥有 admin和root 权限才可以访问 filterChainDefinitionMap.put("/admin/**", "roles[admin,root]"); // 有编辑权限才可以访问 filterChainDefinitionMap.put("/video/update", "perms[video_update]"); // 自定义过滤器 filterChainDefinitionMap.put("/admin/**","roleOrFilter[admin,root]"); // 过滤链是顺序执行,从上而下,一般讲 /** 放到最下面 // authc : url定义必须通过认证才可以访问 // anon : url可以匿名访问 filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean public SecurityManager securityManager(){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 如果不是前后端分离,就不用设置 sessionManager securityManager.setSessionManager(sessionManager()); // redis 整合shiro securityManager.setCacheManager(redisCacheManager()); // 推荐放到最后,不然某些情况下不生效 securityManager.setRealm(customRealm()); return securityManager; } @Bean public CustomRealm customRealm(){ CustomRealm customRealm = new CustomRealm(); // 使用加密方式验证 登录 customRealm.setCredentialsMatcher(hashedCredentialsMatcher()); return customRealm; } @Bean public SessionManager sessionManager(){ CustomSessionManager customSessionManager = new CustomSessionManager(); // 设置session过期时间,默认 30 分钟,单位是 毫秒 customSessionManager.setGlobalSessionTimeout(30*60*1000); // 使用 Redis 自定义sessionDao 来持久化session customSessionManager.setSessionDAO(redisSessionDAO()); return customSessionManager; } /** * 设置密码 加密相关 * @return */ @Bean public HashedCredentialsMatcher hashedCredentialsMatcher(){ HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); // 设置加密方式 hashedCredentialsMatcher.setHashAlgorithmName("md5"); // 设置hash次数 hashedCredentialsMatcher.setHashIterations(2); return hashedCredentialsMatcher; } /** * redis 整合shiro * @return */ @Bean public RedisManager getRedisManager(){ RedisManager redisManager = new RedisManager(); redisManager.setHost("81.69.43.78"); redisManager.setPassword("root"); redisManager.setPort(6379); return redisManager; } @Bean public RedisCacheManager redisCacheManager(){ RedisCacheManager redisCacheManager = new RedisCacheManager(); // 设置过期时间,默认过期时间为 30 分钟,单位秒 redisCacheManager.setExpire(1800); redisCacheManager.setRedisManager(getRedisManager()); return redisCacheManager; } /** * 自定义 Session 的持久化 * @return */ @Bean public RedisSessionDAO redisSessionDAO(){ // 实体类需要实现 Serializable 序列化接口 RedisSessionDAO redisSessionDAO = new RedisSessionDAO(); // 设置默认的超期时间,默认 30 分钟,单位秒 redisSessionDAO.setExpire(1800); redisSessionDAO.setRedisManager(getRedisManager()); // 自定义sessionId 生成 redisSessionDAO.setSessionIdGenerator(new CustomSessionIdGenerator()); return redisSessionDAO; } /** * 管理shiro一些bean的生命周期 即bean初始化 与销毁 * @return */ @Bean public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } /** * 加入注解的使用,不加入这个AOP注解不生效(shiro的注解 例如 @RequiresGuest) * @return */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager()); return authorizationAttributeSourceAdvisor; } /** * 作用: 用来扫描上下文寻找所有的Advistor(通知器), 将符合条件的Advisor应用到切入点的Bean中, * 需要在LifecycleBeanPostProcessor创建后才可以创建 * @return */ @Bean @DependsOn("lifecycleBeanPostProcessor") public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){ DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator(); defaultAdvisorAutoProxyCreator.setUsePrefix(true); return defaultAdvisorAutoProxyCreator; }}
引入maven文件:
org.crazycake shiro-redis 3.1.0
配置文件:
/** * redis 整合shiro * @return */ @Bean public RedisManager getRedisManager(){ RedisManager redisManager = new RedisManager(); redisManager.setHost("81.69.43.78"); redisManager.setPassword("root"); redisManager.setPort(6379); return redisManager; } @Bean public RedisCacheManager redisCacheManager(){ RedisCacheManager redisCacheManager = new RedisCacheManager(); // 设置过期时间,默认过期时间为 30 分钟,单位秒 redisCacheManager.setExpire(1800); redisCacheManager.setRedisManager(getRedisManager()); return redisCacheManager; }
Redis整合SessionManager,管理Session会话
为啥session也要持久化?
@Bean public SessionManager sessionManager(){ CustomSessionManager customSessionManager = new CustomSessionManager(); // 设置session过期时间,默认 30 分钟,单位是 毫秒 customSessionManager.setGlobalSessionTimeout(30*60*1000); // 使用 Redis 自定义sessionDao 来持久化session customSessionManager.setSessionDAO(redisSessionDAO()); return customSessionManager; }/** * 自定义 Session 的持久化 * @return */ @Bean public RedisSessionDAO redisSessionDAO(){ // 实体类需要实现 Serializable 序列化接口 RedisSessionDAO redisSessionDAO = new RedisSessionDAO(); // 设置默认的超期时间,默认 30 分钟,单位秒 redisSessionDAO.setExpire(1800); redisSessionDAO.setRedisManager(getRedisManager()); // 自定义sessionId 生成 redisSessionDAO.setSessionIdGenerator(new CustomSessionIdGenerator()); return redisSessionDAO; }
注意点:
自定义SessionManager
/** * @author zb * @date 2021/2/7 19:04 * @Description: * * SessionManager * * * * DefaultSessionManager: 默认实现,常用于javase * * ServletContainerSessionManager: web环境 * * DefaultWebSessionManager:常用于自定义实现 */public class CustomSessionManager extends DefaultWebSessionManager { /** * 自定义:前端请求传递过滤的请求头名称 */ private static final String AUTHORIZATION = "token"; public CustomSessionManager(){ super(); } @Override protected Serializable getSessionId(ServletRequest request, ServletResponse response) { HttpServletRequest httpServletRequest = WebUtils.toHttp(request); // 自定义请求头 String sessionId = httpServletRequest.getHeader(AUTHORIZATION); if(StrUtil.isNotEmpty(sessionId)){ request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE); //在此处自动将其标记为有效。如果无效,则 //下面的onUnknownSession方法将被调用,届时我们将删除该属性。 request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sessionId); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE); return sessionId; }else { return super.getSessionId(request, response); } }}
/** * @author zb * @date 2021/2/12 17:16 * @Description: 自定义 SessionId */public class CustomSessionIdGenerator implements SessionIdGenerator { @Override public Serializable generateId(Session session) { String s = IdUtil.fastUUID(); return "super"+ s; }}
ShiroConfig常用bean
LifecycleBeanPostProcessor
作用:管理shiro一些bean的生命周期 即bean初始化 与销毁
@Beanpublic LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor();}
AuthorizationAttributeSourceAdvisor
作用:加入注解的使用,不加入这个AOP注解不生效(shiro的注解 例如 @RequiresGuest)
@Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager()); return authorizationAttributeSourceAdvisor;}
DefaultAdvisorAutoProxyCreator
作用: 用来扫描上下文寻找所有的Advistor(通知器), 将符合条件的Advisor应用到切入点的Bean中,需要在LifecycleBeanPostProcessor创建后才可以创建
@Bean@DependsOn("lifecycleBeanPostProcessor")public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){ DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator(); defaultAdvisorAutoProxyCreator.setUsePrefix(true); return defaultAdvisorAutoProxyCreator;}
登录:
@PostMapping("/pub/login") public Maplogin(@RequestBody UserQueryDTO userQueryDTO, HttpServletRequest request, HttpServletResponse response){ // 获取当前主体 Subject subject = SecurityUtils.getSubject(); // 输入账号和密码 UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(userQueryDTO.getUsername(), userQueryDTO.getPassword()); // 设置 rememberMe usernamePasswordToken.setRememberMe(true); // 登录认证 subject.login(usernamePasswordToken); Serializable sessionId = subject.getSession().getId(); List strings = new ArrayList<>(Arrays.asList("1", "2", "3",sessionId.toString())); Map map = new HashMap<>(); map.put("code",200); map.put("message","访问首页成功"); map.put("data",strings); return map; }
退出直接访问 /logout 即可:
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 默认过滤器 【org.apache.shiro.web.filter.mgt DefaultFilter】 // 拦截器路径,部门路径无法进行拦截,时有时无;是因为使用的hashmap,无序的,应该改为LinkHashMap MapfilterChainDefinitionMap = new LinkedHashMap<>(); // 退出过滤器 filterChainDefinitionMap.put("/logout", "logout"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); // 请求头带上token
单体应用到分布式应用下的鉴权方式
电商项目:商品服务,支付服务,用户服务,订单服务…