SpringSecurity中的角色继承问题
今天想和小伙伴们来聊一聊SpringSecurity中的角色继承问题。
角色继承实际上是一个很常见的需求,因为大部分公司治理可能都是金字塔形的,上司可能具备下属的部分甚至所有权限,这一现实场景,反映到我们的代码中,就是角色继承了。Spring Security中为开发者提供了相关的角色继承解决方案,但是这一解决方案在最近的SpringSecurity版本变迁中,使用方法有所变化。今天除了和小伙伴们分享角色继承外,也来顺便说说这种变化,避免小伙伴们踩坑,同时购买了我的书的小伙伴也需要留意,书是基于Spring Boot2.0.4 这个版本写的,这个话题和最新版Spring Boot的还是有一点差别。
1.版本分割线
上文说过,SpringSecurity在角色继承上有两种不同的写法,在Spring Boot2.0.8(对应Spring Security也是5.0.11)上面是一种写法,从Spring Boot2.1.0(对应Spring Security5.1.1)又是另外一种写法,本文将从这两种角度出发,向读者介绍两种不同的角色继承写法。
2.以前的写法
这里说的以前写法,就是指SpringBoot2.0.8(含)之前的写法,在之前的写法中,角色继承只需要开发者提供一个RoleHierarchy接口的实例即可,例如下面这样:
@BeanRoleHierarchy roleHierarchy() { RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl(); String hierarchy = "ROLE_dba > ROLE_admin ROLE_admin > ROLE_user"; roleHierarchy.setHierarchy(hierarchy); return roleHierarchy;}
在这里我们提供了一个RoleHierarchy接口的实例,使用字符串来描述了角色之间的继承关系, ROLE_dba
具备 ROLE_admin
的所有权限,而 ROLE_admin
则具备 ROLE_user
的所有权限,继承与继承之间用一个空格隔开。提供了这个Bean之后,以后所有具备 ROLE_user
角色才能访问的资源, ROLE_dba
和 ROLE_admin
也都能访问,具备 ROLE_amdin
角色才能访问的资源, ROLE_dba
也能访问。
3.现在的写法
但是上面这种写法仅限于SpringBoot2.0.8(含)之前的版本,在之后的版本中,这种写法则不被支持,新版的写法是下面这样:
@BeanRoleHierarchy roleHierarchy() { RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl(); String hierarchy = "ROLE_dba > ROLE_admin n ROLE_admin > ROLE_user"; roleHierarchy.setHierarchy(hierarchy); return roleHierarchy;}
变化主要就是分隔符,将原来用空格隔开的地方,现在用换行符了。这里表达式的含义依然和上面一样,不再赘述。
上面两种不同写法都是配置角色的继承关系,配置完成后,接下来指定角色和资源的对应关系即可,如下:
@Overrideprotected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().antMatchers("/admin/**") .hasRole("admin") .antMatchers("/db/**") .hasRole("dba") .antMatchers("/user/**") .hasRole("user") .and() .formLogin() .loginProcessingUrl("/doLogin") .permitAll() .and() .csrf().disable();}
这个表示 /db/**
格式的路径需要具备dba角色才能访问, /admin/**
格式的路径则需要具备admin角色才能访问, /user/**
格式的路径,则需要具备user角色才能访问,此时提供相关接口,会发现,dba除了访问 /db/**
,也能访问 /admin/**
和 /user/**
,admin角色除了访问 /admin/**
,也能访问 /user/**
,user角色则只能访问 /user/**
。
4.源码分析
这样两种不同的写法,其实也对应了两种不同的解析策略,角色继承关系的解析在RoleHierarchyImpl类的buildRolesReachableInOneStepMap方法中,Spring Boot2.0.8(含)之前该方法的源码如下:
private void buildRolesReachableInOneStepMap() { Pattern pattern = Pattern.compile("(\s*([^\s>]+)\s*>\s*([^\s>]+))"); Matcher roleHierarchyMatcher = pattern .matcher(this.roleHierarchyStringRepresentation); this.rolesReachableInOneStepMap = new HashMap<GrantedAuthority, Set<GrantedAuthority>>(); while (roleHierarchyMatcher.find()) { GrantedAuthority higherRole = new SimpleGrantedAuthority( roleHierarchyMatcher.group(2)); GrantedAuthority lowerRole = new SimpleGrantedAuthority( roleHierarchyMatcher.group(3)); Set<GrantedAuthority> rolesReachableInOneStepSet; if (!this.rolesReachableInOneStepMap.containsKey(higherRole)) { rolesReachableInOneStepSet = new HashSet<>(); this.rolesReachableInOneStepMap.put(higherRole, rolesReachableInOneStepSet); } else { rolesReachableInOneStepSet = this.rolesReachableInOneStepMap .get(higherRole); } addReachableRoles(rolesReachableInOneStepSet, lowerRole); logger.debug("buildRolesReachableInOneStepMap() - From role " + higherRole + " one can reach role " + lowerRole + " in one step."); }}
从这段源码中我们可以看到,角色的继承关系是通过正则表达式进行解析,通过空格进行切分,然后构建相应的map出来。
Spring Boot2.1.0(含)之后该方法的源码如下:
private void buildRolesReachableInOneStepMap() { this.rolesReachableInOneStepMap = new HashMap<GrantedAuthority, Set<GrantedAuthority>>(); try (BufferedReader bufferedReader = new BufferedReader( new StringReader(this.roleHierarchyStringRepresentation))) { for (String readLine; (readLine = bufferedReader.readLine()) != null;) { String[] roles = readLine.split(" > "); for (int i = 1; i < roles.length; i++) { GrantedAuthority higherRole = new SimpleGrantedAuthority( roles[i - 1].replaceAll("^\s+|\s+$", "")); GrantedAuthority lowerRole = new SimpleGrantedAuthority(roles[i].replaceAll("^\s+|\s+$ Set<GrantedAuthority> rolesReachableInOneStepSet; if (!this.rolesReachableInOneStepMap.containsKey(higherRole)) { rolesReachableInOneStepSet = new HashSet<GrantedAuthority>(); this.rolesReachableInOneStepMap.put(higherRole, rolesReachableInOneStepSet); } else { rolesReachableInOneStepSet = this.rolesReachableInOneStepMap.get(higherRole); } addReachableRoles(rolesReachableInOneStepSet, lowerRole); if (logger.isDebugEnabled()) { logger.debug("buildRolesReachableInOneStepMap() - From role " + higherRole + " one can reach role " + lowerRole + " in one step."); } } } } catch (IOException e) { throw new IllegalStateException(e); }}
从这里我们可以看到,这里并没有一上来就是用正则表达式,而是先将角色继承字符串转为一个BufferedReader,然后一行一行的读出来,再进行解析,最后再构建相应的map。从这里我们可以看出为什么前后版本对此有不同的写法。
那么小伙伴在开发过程中,还是需要留意这一个差异。好了,角色继承我们就先说到这里,本文并没有讲SpringSecurity一些基本用法,想了解Spring Security更多用法,敬请留意后续文章。
- Python机器学习的练习六:支持向量机
- [译]Laravel 5.0 之路由缓存
- [译]Laravel 5.0 之 表单验证类 (Form Requests)
- 如何在Python中保存ARIMA时间序列预测模型
- Laravel 5.0 发布, 海量新特性!!
- Python中的白噪声时间训练
- Python机器学习的练习五:神经网络
- 在Python中如何差分时间序列数据集
- 将Keras权值保存为动画视频,更好地了解模型是如何学习的
- TensorFlow:如何通过声音识别追踪蝙蝠
- Python机器学习的练习四:多元逻辑回归
- Python机器学习的练习三:逻辑回归
- 什么?!只用30行代码就能创建一个JavaScript的神经网络?
- ChainerCV: 一个用于深度学习的计算机视觉库
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- Zabbix5.0 问题记录
- 「查缺补漏」巩固你的Nginx知识体系
- 超级帐本合约调用——fabric SDK Node调用TBaaS的合约
- TBaaS合约调用_fabric SDK Node 不使用Connection Profile
- helm安装、使用、实践
- 浅析asp .net core 中间件
- 玩转jenkins - 在自己的服务器上安装jenkins
- CPU有个禁区,内核权限也无法进入!
- Java bytecodes
- 云原生分布式深度学习初探
- 【原创】经验分享:一个Content-Length引发的血案(almost....)
- SAP Spartacus里使用Observable访问Component数据
- SAP Spartacus的b2cLayoutConfig
- python判断工作日,节假日
- Shell高级用法之重定向绑定