Spring Security 中的角色继承问题

SpringSecurity 在角色继承上有两种不同的写法,在 Spring Boot2.0.8(对应 Spring Security 也是 5.0.11)上面是一种写法,从 Spring Boot2.1.0(对应 Spring Security5.1.1)又是另外一种写法。

以前的写法

这里说的以前写法,就是指 SpringBoot2.0.8(含)之前的写法,在之前的写法中,角色继承只需要开发者提供一个 RoleHierarchy 接口的实例即可,例如下面这样:

@Bean
RoleHierarchy 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(含)之前的版本,在之后的版本中,这种写法则不被支持,新版的写法是下面这样:

@Bean
RoleHierarchy roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
String hierarchy = "ROLE_dba > ROLE_admin \n ROLE_admin > ROLE_user";
roleHierarchy.setHierarchy(hierarchy);
return roleHierarchy;
}

变化主要就是分隔符,将原来用空格隔开的地方,现在用换行符了。

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

@Override
protected 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<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。

五:Spring Security 中的角色继承问题的更多相关文章

  1. 看源码,重新审视Spring Security中的角色(roles)是怎么回事

    在网上看见不少的博客.技术文章,发现大家对于Spring Security中的角色(roles)存在较大的误解,最大的误解就是没有搞清楚其中角色和权限的差别(好多人在学习Spring Security ...

  2. Spring Security 解析(五) —— Spring Security Oauth2 开发

    Spring Security 解析(五) -- Spring Security Oauth2 开发   在学习Spring Cloud 时,遇到了授权服务oauth 相关内容时,总是一知半解,因此决 ...

  3. 六:Spring Security 中使用 JWT

    Spring Security 中使用 JWT 1.无状态登录 1.1 什么是有状态? 1.2 什么是无状态 1.3 如何实现无状态 2.JWT 2.1 JWT数据格式 2.2 JWT交互流程 2.3 ...

  4. Spring Security中html页面设置hasRole无效的问题

    Spring Security中html页面设置hasRole无效的问题 一.前言 学了几天的spring Security,偶然发现的hasRole和hasAnyAuthority的区别.当然,可能 ...

  5. Spring Security 中的过滤器

    本文基于 spring-security-core-5.1.1 和 tomcat-embed-core-9.0.12. Spring Security 的本质是一个过滤器链(filter chain) ...

  6. [收藏]Spring Security中的ACL

    ACL即访问控制列表(Access Controller List),它是用来做细粒度权限控制所用的一种权限模型.对ACL最简单的描述就是两个业务员,每个人只能查看操作自己签的合同,而不能看到对方的合 ...

  7. Spring Security 中的 Bcrypt

    最近在写用户管理相关的微服务,其中比较重要的问题是如何保存用户的密码,加盐哈希是一种常见的做法.知乎上有个问题大家可以先读一下: 加盐密码保存的最通用方法是? 对于每个用户的密码,都应该使用独一无二的 ...

  8. 浅谈使用spring security中的BCryptPasswordEncoder方法对密码进行加密与密码匹配

    浅谈使用springsecurity中的BCryptPasswordEncoder方法对密码进行加密(encode)与密码匹配(matches) spring security中的BCryptPass ...

  9. Spring Security中实现微信网页授权

    微信公众号提供了微信支付.微信优惠券.微信H5红包.微信红包封面等等促销工具来帮助我们的应用拉新保活.但是这些福利要想正确地发放到用户的手里就必须拿到用户特定的(微信应用)微信标识openid甚至是用 ...

随机推荐

  1. 一次失败的java Box居中尝试

    效果如下: 通过Box的应该虽然实现了居中,但是页面相当丑!且不能插入JTextField等文本框,总的来说相当失败!!! import javax.swing.Box; import javax.s ...

  2. 5个有趣且不必要的 JavaScipt 技巧

    前一段时间,我创建了一个标题为"7个可爱的Web开发技巧"的帖子.在这里,我描述了一些有趣的技巧,您可以使用3种主要的Web技术之一来实现这些技巧-html,css和JavaScr ...

  3. Spark UDAF实现举例 -- average pooling

    目录 1.UDAF定义 2.向量平均(average pooling) 2.1 average的并行化 2.2 代码实现 2.3 使用 参考 1.UDAF定义 spark中的UDF(UserDefin ...

  4. linux下eclipse

    最近想学习C++,所以就重新安装了linux系统,虽然这两者没有什么联系,但是我还是比较喜欢linux系统,所以在linux下安装了Eclipse,想起了当初学习Red Hat 9.0时的痛苦场景,哎 ...

  5. pthread 读写锁

    pthread 读写锁 (Read Write Lock, rwlock) 把对共享资源的访问者分为读者和写者,读者仅仅对共享资源进行读访问,写者仅仅对共享资源进行写操作. 如果使用互斥量 mutex ...

  6. dhcp分发地址以及静态路由设置

    路由器R1配置: system-view [Huawei]sysname R1 [R1]user-interface console 0 [R1-ui-console0]idle-timeout 0 ...

  7. 安装蓝鲸paas社区版

    安装蓝鲸paas社区版http://docs.bk.tencent.com/bkce_install_guide/setup/get_ready.html#hostssystemctl stop fi ...

  8. 在 Azure 上执行一些简单的 python 工作

    1. 公司禁用了 python 我的主业是桌面开发,偶尔也需要搞搞数据和算法.最近在用 python 处理一些工作,正搞得热火朝天,突然 python 就不能用了,一查记录原来是 IT 管理员禁止我使 ...

  9. 【Oracle】什么是DRM,怎么关闭

    DRM 分析及案例讲解 什么是DRM DRM(Dynamic Resource management)是oracle10.10.2里面推出来的一个新特性,一直到现在最新的12cR1,都存在,且bug非 ...

  10. buuctf刷题之旅—web—WarmUp

    启动靶机 查看源码发现source.php 代码审计,发现hint.php文件 查看hint.php文件(http://7ab330c8-616e-4fc3-9caa-99d9dd66e191.nod ...