原文地址,转载请注明出处: 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中缓存没有清理干净的问题相关推荐

  1. SpringBoot集成Shiro前后端分离使用redis做缓存

    文章目录 一 .shiro介绍 1.基础介绍 2.基本功能点 3.基本流程图 二. 常用的权限管理表关系 2.1. 表组成 2.2. 表结构 三.实战案例 3.1. 案例介绍 3.2. 依赖 3.3. ...

  2. 【Springboot学习】SpringBoot集成Shiro前后端分离使用redis做缓存【个人博客搭建】

    shiro-redis 目录 shiro-redis 下载 shiro-core/jedis 版本对比图 使用前 如何配置? 设置文件 Redis 独立 Redis哨兵 Redis 集群 Spring ...

  3. 关于SpringBoot整合Shiro并入redis缓存

    关于SpringBoot整合Shiro并入redis缓存 最近做一个小项目加入shiro权限框架, Shiro是Apache下的一个开源项目,提供了认证.授权.加密.会话管理,与spring Secu ...

  4. springboot整合shiro + jwt + redis实现权限认证(上手即用)

    目录 前言 项目结构 依赖导入 建数据库表 建表语句 使用插件生成增删改查 添加MyRealm 添加ShiroConfig 添加JwtFilter JWT相关得类 JwtToken JwtAudien ...

  5. SpringBoot整合Shiro搭建登录注册认证授权权限项目模板

    主要内容: 1 SpringBoot整合Shiro安全框架; 2 Shiro主要学习内容总结;(执行流程.主要对象接口.注意事项等) 3 Redis实现对权限信息缓存; ! 温馨提示: 想要快速搭Sh ...

  6. springboot整合shiro

    springboot整合shiro 导入依赖 <!-- shiro鉴权框架--> <dependency><groupId>org.apache.shiro< ...

  7. 补习系列(6)- springboot 整合 shiro 一指禅

    欢迎添加华为云小助手微信(微信号:HWCloud002 或 HWCloud003),输入关键字"加群",加入华为云线上技术讨论群:输入关键字"最新活动",获取华 ...

  8. SpringBoot 整合Shiro 一指禅

    目标 了解ApacheShiro是什么,能做什么: 通过QuickStart 代码领会 Shiro的关键概念: 能基于SpringBoot 整合Shiro 实现URL安全访问: 掌握基于注解的方法,以 ...

  9. SpringBoot整合Shiro实现权限管理与登陆注册

    前言 Shiro解决了什么问题? 互联网无非就是一些用户C想要使用一些服务S的资源去完成某件事,S的资源不能说给谁用就给谁用,因此产生了权限的概念,即C必须有权限才能操作S的资源.S如何确定C就是C呢 ...

最新文章

  1. 【数论基础】模运算详解及其应用
  2. 使用PHP Excel类读取和生成excel文件
  3. 大有可为的GNN:DeepWalk
  4. 数字图像处理——2D降噪
  5. 2017年12月计算机一级c,2017年12月计算机二级C语言考试操作题冲刺卷(2)
  6. 一篇博客读懂设计模式之---委派模式
  7. virtualenv 安装不同版本的虚拟环境的办法
  8. 中英文对照 —— 饮食与美食
  9. steam怎么设置邮箱令牌_steam盗号?这样做50%能够避免损失!
  10. 试验设计第二版茆诗松课后题答案_试验设计习题及答案
  11. 【matlab】拉普拉斯变换与反变换
  12. java中同步代码块具体步骤,Java同步块
  13. PCB设计及硬件编程学习
  14. python局域网监控系统_python 局域网监控
  15. HRA系列DC-DC隔离电源模块接线注意事项
  16. 混淆矩阵与miou代码
  17. hint ksql oracle_性能测试中发现oracle11g数据库每天22点,oralce进程CPU占用率突增
  18. python 单词拆音节_计算一个单词的音节数
  19. 记录openlaw的反爬
  20. 标题: 连接到服务器 ------------------------------ 无法连接到 DESKTOP-TGC5ASS\HAHA。 ----------------------------

热门文章

  1. PHP保留小数的相关方法
  2. NOIP提高模拟-20181019-T1-加密
  3. Sublime Text 3快捷键汇总
  4. webform(八)——LinQ简单增、删、改、查
  5. sql server 提示无法彻底删除_复制-而无法删除数据库或重新配置发布订阅
  6. 高效CSS的一些建议
  7. Python学习笔记:SMTP服务器
  8. Python:高阶函数
  9. 全国计算机网络自学考试,2008年1月全国自考“计算机网络基本原理”试题
  10. [云炬创业学笔记]第一章创业是什么测试12