SpringBoot 基于Shiro + Jwt + Redis的用户权限管理 (三) 鉴权
项目Github地址: https://github.com/baiye21/ShiroDemo
SpringBoot 基于Shiro + Jwt + Redis的用户权限管理 (一) 简介与配置
SpringBoot 基于Shiro + Jwt + Redis的用户权限管理 (二) 认证
SpringBoot 基于Shiro + Jwt + Redis的用户权限管理 (三) 鉴权
一,ShiroFilterFactoryBean配置
FilterChainDefinitionMap排除需要权限才能访问的URL,这样才能进入自定义过滤器JwtFilter中进行后续验证。
比如,测试权限的URl为/role/OneRole.do,它不需要写入FilterChainDefinitionMap中,如下图
二,自定义ShiroSessionManager
认证成功后,将shiro生成的sessionid保存到了access_token中,所以携带token访问时,需要从token将其取出。
com.demo.config.ShiroConfig中
/*** SecurityManager 是 Shiro 架构的核心,通过它来链接Realm和用户(文档中称之为Subject.)*/@Beanpublic SecurityManager securityManager(@Qualifier("passwordRealm") PasswordRealm passwordRealm,@Qualifier("jwtRealm") JwtRealm jwtRealm,@Qualifier("demoRealm") DemoRealm demoRealm,@Qualifier("userModularRealmAuthenticator") UserModularRealmAuthenticator userModularRealmAuthenticator) {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();// 设置realmsecurityManager.setAuthenticator(userModularRealmAuthenticator);List<Realm> realms = new ArrayList<>();// 添加多个realmrealms.add(passwordRealm);realms.add(jwtRealm);realms.add(demoRealm);/** 关闭shiro自带的session,详情见文档* http://shiro.apache.org/session-management.html#SessionManagement-StatelessApplications%28Sessionless%29*/DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();// 不需要将 Shiro Session 中的东西存到任何地方(包括 Http Session 中)defaultSessionStorageEvaluator.setSessionStorageEnabled(false);subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);securityManager.setSubjectDAO(subjectDAO);// 自定义sessionManagersecurityManager.setSessionManager(shiroSessionManager());// securityManager设置自定义认证规则securityManager.setRealms(realms);return securityManager;}@Beanpublic ShiroSessionManager shiroSessionManager() {ShiroSessionManager shiroSessionManager = new ShiroSessionManager();// TODO redis 配置session持久化shiroSessionManager.setSessionDAO(new EnterpriseCacheSessionDAO());return shiroSessionManager;}
com.demo.shiro.ShiroSessionManager
@Slf4j
public class ShiroSessionManager extends DefaultWebSessionManager {public ShiroSessionManager() {super();}private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";@Overrideprotected Serializable getSessionId(ServletRequest request, ServletResponse response) {// 如果请求头中有 AuthorizationString token = WebUtils.toHttp(request).getHeader(Const.TOKEN_HEADER_NAME);if (!StringUtils.isEmpty(token)) {if (JwtUtil.verify(token, Const.TOKEN_SECRET)) {String id = JwtUtil.getClaim(token, Const.JSESSIONID);log.debug("ShiroSessionManager从http header 取出token中的JSESSIONID:{}", id);request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,REFERENCED_SESSION_ID_SOURCE);request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);return id;}return super.getSessionId(request, response);} else {// 否则按默认规则从cookie取sessionIdreturn super.getSessionId(request, response);}}}
三,JwtFilter执行executeLogin();
/*** 执行登录认证* * @param request* @param response* @param mappedValue* @return 是否成功*/@Override// 这个方法判断 尝试进行登录的操作,如果token存在,那么进行提交登录,如果不存在说明可能是正在进行登录或者做其它的事情 直接放过即可protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {try {executeLogin(request, response);return true;} catch (Exception e) {// return false;// throw new AuthenticationException("Token失效请重新登录");// 认证出现异常,传递错误信息msgString msg = e.getMessage();// 获取应用异常(该Cause是导致抛出此throwable(异常)的throwable(异常))Throwable throwable = e.getCause();if (throwable != null && throwable instanceof SignatureVerificationException) {// 该异常为JWT的AccessToken认证失败(Token或者密钥不正确)msg = "token或者密钥不正确(" + throwable.getMessage() + ")";} else if (throwable != null && throwable instanceof TokenExpiredException) {// 该异常为JWT的AccessToken已过期(TokenExpiredException),// 判断RefreshToken未过期就进行AccessToken刷新if (this.refreshToken(request, response)) {return true;} else {msg = "token已过期(" + throwable.getMessage() + ")";}} else {// 应用异常不为空if (throwable != null) {// 获取应用异常msgmsg = throwable.getMessage();}}/*** 错误两种处理方式* 1. 将非法请求转发到/401的Controller处理,抛出自定义无权访问异常被全局捕捉再返回Response信息* 2. 无需转发,直接返回Response信息 一般使用第二种(更方便)*/// 直接返回Response信息this.response401(request, response, msg);return false;}}
四,UserModularRealmAuthenticator加载DemoRealm
login的时候创建的是CustomizedToken,之后的请求Header携带的都是JwtToken,因此区分加载哪一种类型的Realm。
if(authenticationToken instanceof JwtToken) {log.debug("验证的Token类型是:{}", "JwtToken");typeRealms.clear();// 获取header部的token进行强制类型转换JwtToken jwtToken = (JwtToken) authenticationToken;for (Realm realm : realms) {if (realm.getName().contains("Demo")) {typeRealms.add(realm);}}return doSingleRealmAuthentication(typeRealms.iterator().next(), jwtToken);}
五,DemoRealm进行鉴权(doGetAuthorizationInfo)
/*** 功能: 获取用户权限信息,包括角色以及权限。只有当触发检测用户权限时才会调用此方法,例如checkRole,checkPermission** @param principals* @return AuthorizationInfo 权限信息*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {log.info("demoRealm doGetAuthorizationInfo 用户赋权 ");String userid = null;if (principals != null) {// 此处的principals为 UserMasterObject PrimaryPrincipal = principals.getPrimaryPrincipal();if (PrimaryPrincipal instanceof UserMaster) {UserMaster userMaster = (UserMaster) PrimaryPrincipal;userid = userMaster.getUserId();} else {// 此处的principals为tokenuserid = JwtUtil.getClaim(principals.toString(), Const.TOKEN_CLAIM_USERID);}}SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();// 获取用户角色与权限信息UserAccessInfo userAccessInfo = iUserService.getUserAccessInfo(userid);/*** 设置用户拥有的角色集合,<br>* * accountType = 1 管理员 admin <br>* accountType = 2 领导 Leader <br>* accountType = 3 普通用户 user <br>* */info.setRoles(userAccessInfo.getRoleSet());// 设置用户拥有的权限集合info.addStringPermissions(userAccessInfo.getPermissionSet());return info;}
六,getUserAccessInfo
com.demo.service.impl.UserServiceImpl的getUserAccessInfo方法
/*** 获取用户角色set与权限set** @param userId* @return*/public UserAccessInfo getUserAccessInfo(String userId) {UserAccessInfo userAccessInfo = new UserAccessInfo();// 用户角色setSet<String> roleSet = new HashSet<String>();// 用户权限setSet<String> permissionSet = new HashSet<String>();UserMaster userMaster = userMasterMapper.selectByUserId(userId);// 账号类型String accountType = userMaster.getAccountType();// 管理员 accountType = 1if (Const.ADMIN_USER_CODE.equals(accountType)) {// adminroleSet.add(Const.ADMIN_USER);// Leader accountType = 2} else if (Const.LEADWER_USER_CODE.equals(accountType)) {// LeaderroleSet.add(Const.LEADER_USER);// 普通用户 accountType = 3} else if (Const.NORMAL_USER_CODE.equals(accountType)) {// userroleSet.add(Const.NORMAL_USER);}// 用户权限类型String permissionType = userMaster.getPermissionType();if (Const.LEVEL_001_CODE.equals(permissionType)) {permissionSet.add(Const.LEVEL_001);} else if (Const.LEVEL_002_CODE.equals(permissionType)) {permissionSet.add(Const.LEVEL_002);} else if (Const.LEVEL_003_CODE.equals(permissionType)) {permissionSet.add(Const.LEVEL_003);} else if (Const.LEVEL_004_CODE.equals(permissionType)) {permissionSet.add(Const.LEVEL_004);} else if (Const.LEVEL_005_CODE.equals(permissionType)) {permissionSet.add(Const.LEVEL_005);}userAccessInfo.setRoleSet(roleSet);userAccessInfo.setPermissionSet(permissionSet);return userAccessInfo;}
七,简单测试
测试用的TestRoleController ,主要测试以下权限:
ps:由于之前测试用户表设计一个用户只对应一种角色一种权限,所以第四个测试只有失败的情况
- @RequiresRoles(Const.ADMIN_USER) 角色-- 只有管理员用户才能访问
- @RequiresRoles(value = { Const.ADMIN_USER, Const.LEADER_USER }, logical = Logical.OR) 角色-- 管理员用户和Leader用户都能访问
- @RequiresPermissions(Const.LEVEL_004) 权限-- 001 能访问
- @RequiresPermissions(value = {Const.LEVEL_004,Const.LEVEL_005}, logical = Logical.AND) 权限-- 004 且 005 才能访问
/** Author : baiye <baiye_21@163.com>Time : 2021/06/30Function:
*/
@RestController
@RequestMapping("/role/")
public class TestRoleController {/*** 只有管理员用户才能访问** @return* @throws Exception*/@RequiresRoles(Const.ADMIN_USER)@RequestMapping(value = "/OneRole.do", method = RequestMethod.POST)@ResponseBodypublic ServerResponse<String> OneRole() throws Exception {return ServerResponse.createBySuccessMessage("One Role");}/*** 管理员用户和Leader用户能访问** @return* @throws Exception*/@RequiresRoles(value = { Const.ADMIN_USER, Const.LEADER_USER }, logical = Logical.OR)@RequestMapping(value = "/TwoRole.do", method = RequestMethod.POST)@ResponseBodypublic ServerResponse<String> TwoRole() throws Exception {return ServerResponse.createBySuccessMessage("Two Role");}/*** Permission 001 能访问** @return* @throws Exception*/@RequiresPermissions(Const.LEVEL_004)@RequestMapping(value = "/OnePermission.do", method = RequestMethod.POST)@ResponseBodypublic ServerResponse<String> OnePermission() throws Exception {return ServerResponse.createBySuccessMessage("One Permission");}/*** Permission 004 且 005 能访问** @return* @throws Exception*/@RequiresPermissions(value = {Const.LEVEL_004,Const.LEVEL_005}, logical = Logical.AND)@RequestMapping(value = "/TwoPermission.do", method = RequestMethod.POST)@ResponseBodypublic ServerResponse<String> TwoPermission() throws Exception {return ServerResponse.createBySuccessMessage("Two Permission");}
}
准备的测试用户
使用demo002用户登录
localhost:9999/role/OneRole.do
localhost:9999/role/TwoRole.do
localhost:9999/role/OnePermission.do
localhost:9999/role/TwoPermission.do
SpringBoot 基于Shiro + Jwt + Redis的用户权限管理 (三) 鉴权相关推荐
- Shiro+JWT+Redis实现用户校验
Shiro基础知识 1. shiro基本功能 认证:验证用户登录认证: 授权:即权限验证,对已经登录的用户验证是否有相应的权限: 会话管理:用户在认证成功之后创建会话,当前用户的所有信息都会保存在这个 ...
- PMS权限管理和鉴权过程
一.权限的管理基础知识 1.系统的权限机制分为:权限解析.权限分配.鉴权.动态添加权限 2.PermissionInfo : PackageParser.Permission中包含一个对应的Perm ...
- Android pms权限管理,PMS权限管理和鉴权过程
一.权限的管理基础知识 1.系统的权限机制分为:权限解析.权限分配.鉴权.动态添加权限 2.PermissionInfo : PackageParser.Permission中包含一个对应的Perm ...
- springboot整合shiro + jwt + redis实现权限认证(上手即用)
目录 前言 项目结构 依赖导入 建数据库表 建表语句 使用插件生成增删改查 添加MyRealm 添加ShiroConfig 添加JwtFilter JWT相关得类 JwtToken JwtAudien ...
- SpringBoot2.0 整合 Shiro 框架,实现用户权限管理
GitHub源码地址:知了一笑 https://github.com/cicadasmile/middle-ware-parent 一.Shiro简介 1.基础概念 Apache Shiro是一个强大 ...
- 基于shiro+jwt的真正rest url权限管理,前后端分离
代码地址如下: http://www.demodashi.com/demo/13277.html bootshiro & usthe bootshiro是基于springboot+shiro+ ...
- springBoot中shiro与Redis整合的配置文件
springBoot中shiro与Redis整合的配置文件 整合依赖: < ...
- 关于SpringBoot整合Shiro并入redis缓存
关于SpringBoot整合Shiro并入redis缓存 最近做一个小项目加入shiro权限框架, Shiro是Apache下的一个开源项目,提供了认证.授权.加密.会话管理,与spring Secu ...
- 基于LDAP的WebLogic虚拟化统一用户权限管理
0 引言 随着国家电网SG186工程建设的深入,企业门户的建设也开始由省电力公司一级向地市供电公司一级延伸.根据国家电网门户典型设计,河南省电力公司采用了虚拟方式建设全省各地市门户.由于虚拟门户的软. ...
最新文章
- java云服务器系统选择,java云服务器系统选择
- IIRF重写在asp.net4.0+IIS6中部分失败的解决方案
- 对称密码算法Rijndael解析—加密
- yii2 windows 安装
- Qt多线程 TCP 服务端
- [攻防世界 pwn]——pwn1(内涵peak小知识)
- 757计算机电子元件,飞行员的好帮手 波音757的发动机指示与机组报警系统简介...
- python requests_一起看看Python之Requests库
- 计算机课程设计大全,单片机课程设计题目大全计算机软件及应用IT计算机专业资料-单片(10页)-原创力文档...
- abaqus模拟单向压缩实验
- edge浏览器设置启动时默认打开新标签页
- userAgent判断客户端,以及各个浏览器的ua
- c#语言猜数字游戏,C#实现猜数字游戏
- JAVA电商 B2B2C商城系统 多用户商城系统 直播带货 新零售商城 o2o商城 电子商务 拼团商城 分销商城 直播商城 短视频商城 springcloud商城 spring cloud商城
- Linux运维工程师学习大纲
- 共享红色理念创业计划书PPT模板
- 艾默生流量计校验功能
- MySQL免安装最新2021详细教程
- ubuntu16.04下编译ORB_SLAM3时出现的问题及解决方法
- 浏览器打开新窗口被拦截问题