SpringSecurity中的角色继承问题

2年前 (2022) 程序员胖胖胖虎阿
423 0 0

今天想和小伙伴们来聊一聊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接口的实例即可,例如下面这样:

  
  
  
  1. @Bean

  2. RoleHierarchy roleHierarchy() {

  3. RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();

  4. String hierarchy = "ROLE_dba > ROLE_admin ROLE_admin > ROLE_user";

  5. roleHierarchy.setHierarchy(hierarchy);

  6. return roleHierarchy;

  7. }

在这里我们提供了一个RoleHierarchy接口的实例,使用字符串来描述了角色之间的继承关系, ROLE_dba具备 ROLE_admin的所有权限,而 ROLE_admin则具备 ROLE_user的所有权限,继承与继承之间用一个空格隔开。提供了这个Bean之后,以后所有具备 ROLE_user角色才能访问的资源, ROLE_dbaROLE_admin也都能访问,具备 ROLE_amdin角色才能访问的资源, ROLE_dba也能访问。

3.现在的写法

但是上面这种写法仅限于SpringBoot2.0.8(含)之前的版本,在之后的版本中,这种写法则不被支持,新版的写法是下面这样:

  
  
  
  1. @Bean

  2. RoleHierarchy roleHierarchy() {

  3. RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();

  4. String hierarchy = "ROLE_dba > ROLE_admin \n ROLE_admin > ROLE_user";

  5. roleHierarchy.setHierarchy(hierarchy);

  6. return roleHierarchy;

  7. }

变化主要就是分隔符,将原来用空格隔开的地方,现在用换行符了。这里表达式的含义依然和上面一样,不再赘述。

上面两种不同写法都是配置角色的继承关系,配置完成后,接下来指定角色和资源的对应关系即可,如下:

  
  
  
  1. @Override

  2. protected void configure(HttpSecurity http) throws Exception {

  3. http.authorizeRequests().antMatchers("/admin/**")

  4. .hasRole("admin")

  5. .antMatchers("/db/**")

  6. .hasRole("dba")

  7. .antMatchers("/user/**")

  8. .hasRole("user")

  9. .and()

  10. .formLogin()

  11. .loginProcessingUrl("/doLogin")

  12. .permitAll()

  13. .and()

  14. .csrf().disable();

  15. }

这个表示 /db/**格式的路径需要具备dba角色才能访问, /admin/**格式的路径则需要具备admin角色才能访问, /user/**格式的路径,则需要具备user角色才能访问,此时提供相关接口,会发现,dba除了访问 /db/**,也能访问 /admin/**/user/**,admin角色除了访问 /admin/**,也能访问 /user/**,user角色则只能访问 /user/**

4.源码分析

这样两种不同的写法,其实也对应了两种不同的解析策略,角色继承关系的解析在RoleHierarchyImpl类的buildRolesReachableInOneStepMap方法中,Spring Boot2.0.8(含)之前该方法的源码如下:

  
  
  
  1. private void buildRolesReachableInOneStepMap() {

  2. Pattern pattern = Pattern.compile("(\\s*([^\\s>]+)\\s*>\\s*([^\\s>]+))");

  3. Matcher roleHierarchyMatcher = pattern

  4. .matcher(this.roleHierarchyStringRepresentation);

  5. this.rolesReachableInOneStepMap = new HashMap<GrantedAuthority, Set<GrantedAuthority>>();

  6. while (roleHierarchyMatcher.find()) {

  7. GrantedAuthority higherRole = new SimpleGrantedAuthority(

  8. roleHierarchyMatcher.group(2));

  9. GrantedAuthority lowerRole = new SimpleGrantedAuthority(

  10. roleHierarchyMatcher.group(3));

  11. Set<GrantedAuthority> rolesReachableInOneStepSet;

  12. if (!this.rolesReachableInOneStepMap.containsKey(higherRole)) {

  13. rolesReachableInOneStepSet = new HashSet<>();

  14. this.rolesReachableInOneStepMap.put(higherRole,

  15. rolesReachableInOneStepSet);

  16. }

  17. else {

  18. rolesReachableInOneStepSet = this.rolesReachableInOneStepMap

  19. .get(higherRole);

  20. }

  21. addReachableRoles(rolesReachableInOneStepSet, lowerRole);

  22. logger.debug("buildRolesReachableInOneStepMap() - From role " + higherRole

  23. + " one can reach role " + lowerRole + " in one step.");

  24. }

  25. }

从这段源码中我们可以看到,角色的继承关系是通过正则表达式进行解析,通过空格进行切分,然后构建相应的map出来。

Spring Boot2.1.0(含)之后该方法的源码如下:

  
  
  
  1. private void buildRolesReachableInOneStepMap() {

  2. this.rolesReachableInOneStepMap = new HashMap<GrantedAuthority, Set<GrantedAuthority>>();

  3. try (BufferedReader bufferedReader = new BufferedReader(

  4. new StringReader(this.roleHierarchyStringRepresentation))) {

  5. for (String readLine; (readLine = bufferedReader.readLine()) != null;) {

  6. String[] roles = readLine.split(" > ");

  7. for (int i = 1; i < roles.length; i++) {

  8. GrantedAuthority higherRole = new SimpleGrantedAuthority(

  9. roles[i - 1].replaceAll("^\\s+|\\s+$", ""));

  10. GrantedAuthority lowerRole = new SimpleGrantedAuthority(roles[i].replaceAll("^\\s+|\\s+$

  11. Set<GrantedAuthority> rolesReachableInOneStepSet;

  12. if (!this.rolesReachableInOneStepMap.containsKey(higherRole)) {

  13. rolesReachableInOneStepSet = new HashSet<GrantedAuthority>();

  14. this.rolesReachableInOneStepMap.put(higherRole, rolesReachableInOneStepSet);

  15. } else {

  16. rolesReachableInOneStepSet = this.rolesReachableInOneStepMap.get(higherRole);

  17. }

  18. addReachableRoles(rolesReachableInOneStepSet, lowerRole);

  19. if (logger.isDebugEnabled()) {

  20. logger.debug("buildRolesReachableInOneStepMap() - From role " + higherRole

  21. + " one can reach role " + lowerRole + " in one step.");

  22. }

  23. }

  24. }

  25. } catch (IOException e) {

  26. throw new IllegalStateException(e);

  27. }

  28. }

从这里我们可以看到,这里并没有一上来就是用正则表达式,而是先将角色继承字符串转为一个BufferedReader,然后一行一行的读出来,再进行解析,最后再构建相应的map。从这里我们可以看出为什么前后版本对此有不同的写法。

那么小伙伴在开发过程中,还是需要留意这一个差异。好了,角色继承我们就先说到这里,本文并没有讲SpringSecurity一些基本用法,想了解Spring Security更多用法,敬请留意后续文章。

加入我的星球,和众多大牛一起切磋技术推荐一个技术圈子,Java技能提升就靠它了。

SpringSecurity中的角色继承问题


往期精彩回顾

2019新年福利,新书免费送!
Docker教程
Redis教程
SpringCloud教程
Git教程
MongoDB教程
SpringBoot+Vue前后端分离开源项目-微人事
SpringBoot+Vue前后端分离开源项目-V部落

SpringSecurity中的角色继承问题

本文分享自微信公众号 - 江南一点雨(a_javaboy)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

版权声明:程序员胖胖胖虎阿 发表于 2022年9月22日 上午11:48。
转载请注明:SpringSecurity中的角色继承问题 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...