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. css 自定义悬浮窗写法

    HTML: css(sass): 复制代码 .info { } &:hover::after { content: attr(data-title); display: inline-bloc ...

  2. 表单综合HTML

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...

  3. light-rtc: 理念与实践

    在与同行交流过程中,发现很多同行对 WebRTC 改动太多,导致无法升级 WebRTC 版本.而 WebRTC 开源社区的快速迭代,让他们感到欣喜又焦虑:开源社区的迭代效果,是不是超过了他们对 Web ...

  4. Java中几种常见的循环

    多重if_else: package com.dengchaoqun.ht; public class Double_For02 { /** * * 打印乘法表 */ public static vo ...

  5. 再看C语言-算法

    通常一个程序包括算法.数据结构.程序设计方法及语言工具和环境这四个方面.其中算法是核心,算法就是解决"做什么"和"如何做"的问题.算法是程序的灵魂,项目中如果接 ...

  6. spark-streaming-连接kafka的两种方式

    推荐系统的在线部分往往使用spark-streaming实现,这是一个很重要的环节. 在线流程的实时数据一般是从kafka获取消息到spark streaming spark连接kafka两种方式在面 ...

  7. Apache伪静态(Rewrite).htaccess文件详解

    Htaccess(超文本访问)是一个简单的配置文件,它允许设计师,开发者和程序员通过它来改变Apache Web服务器的配置.这些功能包括用户重定向.URL重写(url rewrite,国内很多称为伪 ...

  8. 并发编程--锁--Lock和Synchronized

    说说对于 synchronized 关键字的了解? synchronized关键字解决的是多个线程之间访问资源的同步性: synchronized 关键字可以保证被它修饰的方法或者代码块在任意时刻只能 ...

  9. 【JavaWeb】JSTL 标签库

    JSTL 标签库 简介 JSTL(JSP Standard Tag Library),即 JSP 标准标签库.标签库是为了替换代码脚本,使得整个 jsp 页面变得更加简洁. JSTL 有五个功能不同的 ...

  10. 剑指offer 面试题10.1:青蛙跳台阶

    题目描述 一只青蛙一次可以跳上1级台阶,也可以跳上2级.求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果). 编程思想 对于本题,前提只有 一次 1阶或者2阶的跳法.a.如果两种跳 ...