springboot整合shiro-关于登出时,redis中缓存没有清理干净的问题
原文地址,转载请注明出处: https://blog.csdn.net/qq_34021712/article/details/84722724 ©王赛超
如果是跟着我的shiro系列博客敲下来的,其实还有一个bug,这是一个网友遇到的,他在登出的时候,发现redis中当前用户身份认证缓存没有清理掉,之前在 springboot整合shiro-ehcache缓存(五) 中测试添加权限之后,清理的是所有用户的缓存,所以没有发现这个问题。
还记得上一篇博客: springboot整合shiro-实现自己登出(十五), 我们在登出方法中,清理了当前用户的 身份认证 和 权限认证的 缓存信息,最后发现有一个key 没有清理掉,如下图:
为什么 该key没有清除掉呢?经过debug发现,在清理 身份认证 缓存的时候,调用了ShiroRealm的clearCachedAuthenticationInfo 最终调用到 RedisCache的 remove 方法,但是传过来key 却是 User实体,为什么会是User实体,就是因为在 ShiroRealm 的 doGetAuthenticationInfo 方法返回值 SimpleAuthenticationInfo 中,第一个参数 传的是 User实体,具体 debug细节 如下:
而在删除 用户 权限缓存时,却没有这个问题,删除缓存时,传入的key为SimplePrincipalCollection 最终调用getRedisKeyFromPrincipalIdField 根据你在 ShiroConfig中 配置 RedisCacheManager 指定的那个字段作为缓存的前缀,根据反射获取该字段的值 并返回,具体的配置信息,可以参考之前博客的源码:
解决方案
第一种,判断key为User实体,强转并获取用户名
第二种,将ShiroRealm 的 doGetAuthenticationInfo 方法返回值 SimpleAuthenticationInfo 中,第一个参数,传username,不要传User实体
第一步:修改ShiroRealm
修改shiroRealm中的 doGetAuthenticationInfo 验证用户身份的最后一句 返回值
return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(),new MyByteSource(user.getUsername()),getName());
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {//获取用户名密码 第一种方式//String username = (String) authenticationToken.getPrincipal();//String password = new String((char[]) authenticationToken.getCredentials());//获取用户名 密码 第二种方式UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;String username = usernamePasswordToken.getUsername();String password = new String(usernamePasswordToken.getPassword());//从数据库查询用户信息User user = this.userMapper.findByUserName(username);//可以在这里直接对用户名校验,或者调用 CredentialsMatcher 校验if (user == null) {throw new UnknownAccountException("用户名或密码错误!");}//这里将 密码对比 注销掉,否则 无法锁定 要将密码对比 交给 密码比较器//if (!password.equals(user.getPassword())) {// throw new IncorrectCredentialsException("用户名或密码错误!");//}if ("1".equals(user.getState())) {throw new LockedAccountException("账号已被锁定,请联系管理员!");}SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(),new MyByteSource(user.getUsername()),getName());return info;
}
并且修改shiroRealm中 的 doGetAuthorizationInfo 方法,SecurityUtils.getSubject().getPrincipal()之前 返回的是User实体,现在 就是我们上一步中 放进去的用户名。并且这里需要再单独根据 username去数据库查询 User
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {System.out.println("查询权限方法调用了!!!");//这里获取到的就是 上面方法放进去的username了String username = (String)SecurityUtils.getSubject().getPrincipal();//需要单独根据 username 从数据库查询用户信息User user = this.userMapper.findByUserName(username);//获取用户角色Set<Role> roles =this.roleMapper.findRolesByUserId(user.getUid());//添加角色SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();for (Role role : roles) {authorizationInfo.addRole(role.getRole());}//获取用户权限Set<Permission> permissions = this.permissionMapper.findPermissionsByRoleId(roles);//添加权限for (Permission permission:permissions) {authorizationInfo.addStringPermission(permission.getPermission());}return authorizationInfo;
}
第二步:修改项目中 其他地方 使用 (User)SecurityUtils.getSubject().getPrincipal();的 代码
因为 我们在第一部中 已经将 Principal 从 User实体 改为了 username 所以这些相应的都需要修改,这里要看你们代码中都是在那里使用的了。
第三步:修改之前 index.html页面中的34行 <shiro:principal property=“username”/>
因为 principal为 用户名,已经不是实体了,这里再指定 property 就会报如下 异常:
2018/11/30 15:13:28.861 org.thymeleaf.TemplateEngine [] ERROR [THYMELEAF][http-nio-9090-exec-5] Exception processing template "index": Error during execution of processor 'at.pollux.thymeleaf.shiro.processor.element.HasPermissionElementProcessor' (index:15)
2018/11/30 15:13:28.862 o.s.web.servlet.DispatcherServlet [] DEBUG Error rendering view [org.thymeleaf.spring4.view.ThymeleafView@7578de10] in DispatcherServlet with name 'dispatcherServlet'
org.thymeleaf.exceptions.TemplateProcessingException: Error during execution of processor 'at.pollux.thymeleaf.shiro.processor.element.HasPermissionElementProcessor' (index:15)
具体如下图,只需要使用 shiro:principal</shiro:principal> 就可以了。
第四步:修改RedisCache 类中的 getStringRedisKey 方法
直接返回 key.toString 下面的 getRedisKeyFromPrincipalIdField方法 也可以直接删除了,因为不再使用它了 ,原本 它存在的意义 就是为了解决 principal放的是 User实体。
第五步:启动测试
将redis清空, 并将浏览器缓存清除,启动项目测试,问题已解决,至于另外两个key都是我们自定义的功能,如果想要删除的话,直接删除redis的key就行了 都是使用username 拼接的key。
管理员清理其他用户的缓存
还有这样一种情况,有test 和 admin两名用户, admin是管理员,在给 test用户分配新的权限之后,需要清除该用户的 权限缓存信息 ,这里有一种笨方法,如下 ,添加一个 删除用户缓存的方法, 只有 userInfo:clearCache 权限才可以执行此操作 ,并将 该权限给admin用户,在admin用户 给test用户 分配新的权限之后,可以立即清除test用户的权限缓存。
/*** 将该权限赋给 admin用户 使用admin用户清理其他用户的 权限缓存* @param username* @return*/
@RequiresPermissions("userInfo:clearCache")
@RequestMapping(value = "/clearCache",method = RequestMethod.GET)
@ResponseBody
public String clearCache(String username) {String[] keys = new String[3];keys[0] = "shiro:cache:authenticationCache:"+username;keys[1] = "shiro:cache:authorizationCache:"+username;keys[2] = "shiro:cache:retrylimit:"+username;//原子性 命令 删除多个keyshiroRedisTemplate.delete(CollectionUtils.arrayToList(keys));return "删除"+username+"权限成功";}
网友给出的更简洁方案:在自定义的Realm中重写下面这两个方法。
/*** 建议重写此方法,提供唯一的缓存Key*/@Overrideprotected Object getAuthorizationCacheKey(PrincipalCollection principals) {UserInfoDO user = (UserInfoDO) principals.getPrimaryPrincipal();return user.getUserName();
// return super.getAuthorizationCacheKey(principals);}/*** 建议重写此方法,提供唯一的缓存Key*/@SuppressWarnings("unchecked")@Overrideprotected Object getAuthenticationCacheKey(PrincipalCollection principals) {UserInfoDO user = (UserInfoDO) principals.getPrimaryPrincipal();return user.getUserName();
// return super.getAuthenticationCacheKey(principals);}
springboot整合shiro-关于登出时,redis中缓存没有清理干净的问题相关推荐
- SpringBoot集成Shiro前后端分离使用redis做缓存
文章目录 一 .shiro介绍 1.基础介绍 2.基本功能点 3.基本流程图 二. 常用的权限管理表关系 2.1. 表组成 2.2. 表结构 三.实战案例 3.1. 案例介绍 3.2. 依赖 3.3. ...
- 【Springboot学习】SpringBoot集成Shiro前后端分离使用redis做缓存【个人博客搭建】
shiro-redis 目录 shiro-redis 下载 shiro-core/jedis 版本对比图 使用前 如何配置? 设置文件 Redis 独立 Redis哨兵 Redis 集群 Spring ...
- 关于SpringBoot整合Shiro并入redis缓存
关于SpringBoot整合Shiro并入redis缓存 最近做一个小项目加入shiro权限框架, Shiro是Apache下的一个开源项目,提供了认证.授权.加密.会话管理,与spring Secu ...
- springboot整合shiro + jwt + redis实现权限认证(上手即用)
目录 前言 项目结构 依赖导入 建数据库表 建表语句 使用插件生成增删改查 添加MyRealm 添加ShiroConfig 添加JwtFilter JWT相关得类 JwtToken JwtAudien ...
- SpringBoot整合Shiro搭建登录注册认证授权权限项目模板
主要内容: 1 SpringBoot整合Shiro安全框架; 2 Shiro主要学习内容总结;(执行流程.主要对象接口.注意事项等) 3 Redis实现对权限信息缓存; ! 温馨提示: 想要快速搭Sh ...
- springboot整合shiro
springboot整合shiro 导入依赖 <!-- shiro鉴权框架--> <dependency><groupId>org.apache.shiro< ...
- 补习系列(6)- springboot 整合 shiro 一指禅
欢迎添加华为云小助手微信(微信号:HWCloud002 或 HWCloud003),输入关键字"加群",加入华为云线上技术讨论群:输入关键字"最新活动",获取华 ...
- SpringBoot 整合Shiro 一指禅
目标 了解ApacheShiro是什么,能做什么: 通过QuickStart 代码领会 Shiro的关键概念: 能基于SpringBoot 整合Shiro 实现URL安全访问: 掌握基于注解的方法,以 ...
- SpringBoot整合Shiro实现权限管理与登陆注册
前言 Shiro解决了什么问题? 互联网无非就是一些用户C想要使用一些服务S的资源去完成某件事,S的资源不能说给谁用就给谁用,因此产生了权限的概念,即C必须有权限才能操作S的资源.S如何确定C就是C呢 ...
最新文章
- 【数论基础】模运算详解及其应用
- 使用PHP Excel类读取和生成excel文件
- 大有可为的GNN:DeepWalk
- 数字图像处理——2D降噪
- 2017年12月计算机一级c,2017年12月计算机二级C语言考试操作题冲刺卷(2)
- 一篇博客读懂设计模式之---委派模式
- virtualenv 安装不同版本的虚拟环境的办法
- 中英文对照 —— 饮食与美食
- steam怎么设置邮箱令牌_steam盗号?这样做50%能够避免损失!
- 试验设计第二版茆诗松课后题答案_试验设计习题及答案
- 【matlab】拉普拉斯变换与反变换
- java中同步代码块具体步骤,Java同步块
- PCB设计及硬件编程学习
- python局域网监控系统_python 局域网监控
- HRA系列DC-DC隔离电源模块接线注意事项
- 混淆矩阵与miou代码
- hint ksql oracle_性能测试中发现oracle11g数据库每天22点,oralce进程CPU占用率突增
- python 单词拆音节_计算一个单词的音节数
- 记录openlaw的反爬
- 标题: 连接到服务器 ------------------------------ 无法连接到 DESKTOP-TGC5ASS\HAHA。 ----------------------------