SpringSecurity的角色继承分析
创始人
2024-12-23 11:22:46
0次

今天想和小伙伴们来聊一聊 Spring Security 中的角色继承问题。

角色继承实际上是一个很常见的需求,因为大部分公司治理可能都是金字塔形的,上司可能具备下属的部分甚至所有权限,这一现实场景,反映到我们的代码中,就是角色继承了。

Spring Security 中为开发者提供了相关的角色继承解决方案,但是这一解决方案在最近的 Spring Security 版本变迁中,使用方法有所变化。今天除了和小伙伴们分享角色继承外,也来顺便说说这种变化,避免小伙伴们踩坑,同时购买了我的书的小伙伴也需要留意,书是基于 Spring Boot2.0.4 这个版本写的,这个话题和最新版 Spring Boot 的还是有一点差别。

以前的写法

这里说的以前写法,就是指 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 也能访问。

现在的写法

但是上面这种写法仅限于 Spring Boot2.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/** 。

源码分析

这样两种不同的写法,其实也对应了两种不同的解析策略,角色继承关系的解析在 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>();while (roleHierarchyMatcher.find()) {GrantedAuthority higherRole = new SimpleGrantedAuthority(roleHierarchyMatcher.group(2));GrantedAuthority lowerRole = new SimpleGrantedAuthority(roleHierarchyMatcher.group(3));Set 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>();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 rolesReachableInOneStepSet;if (!this.rolesReachableInOneStepMap.containsKey(higherRole)) {rolesReachableInOneStepSet = new HashSet();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。从这里我们可以看出为什么前后版本对此有不同的写法。

相关内容

热门资讯

空调连续工作多久需要停机休息 空调连续工作的时间取决于多个因素,包括空调的型号、功率、使用环境和维护情况等。一般来说,空调可以连续...
空调哪个符号是除湿的 空调除湿功能通常用一个水滴形状的符号表示。不同品牌和型号的空调遥控器可能会有所差异,但一般来说,除湿...
格力空调多长时间加一次氟利昂 1、一般情况下,空调对氟利昂的使用量是非常小的,所以在空调使用了四年左右的时候再考虑加也没问题。2、...
空调用多长时间需要加氟一次 一般情况下是五年。当出现以下这几种情况时,空调才需要加氟。1、空调的室内机和室外机各有两个接口,当接...
格调生活·C标思享荟:卡萨帝在... 9月12日的郑州龙湖金融岛,夜风轻拂,灯光璀璨。“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑...
品牌价值破千亿、高端份额持续领... 9月12日,“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑州站”在郑州龙湖金融岛举办。2026...
空调主板坏了修800还是换新的 空调主板坏了是修还是换新,需要考虑多个因素,以下是一些建议:1. 维修成本:如果维修费用较低,比如 ...
空调开多久需要休息多长时间 问题:空调开多久需要休息多长时间回答:1、在使用空调的过程中,一般空调开7~8个小时左右需要休息2~...
中央空调多长时间清洗一次 1、一般空调开机前清洗一次即可,空调开机中间时段再清洗一次,空调关机时清洗一次,这样比较合理。2、空...
中央空调氟利昂多久加一次 看空调制热和制冷程度,很多人的观点认为氟利昂必须一年放一次,其实这个观点是错误的,理解上面有点偏差,...