Spring Security角色继承分析

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

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

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

以前的写法

这里说的以前写法,就是指 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。从这里我们可以看出为什么前后版本对此有不同的写法。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 自定义Spring Security的身份验证失败处理方法

    1.概述 在本快速教程中,我们将演示如何在Spring Boot应用程序中自定义Spring Security的身份验证失败处理.目标是使用表单登录方法对用户进行身份验证. 2.认证和授权(Authentication and Authorization) 身份验证和授权通常结合使用,因为它们在授予系统访问权限时起着重要且同样重要的作用. 但是,它们具有不同的含义,并在验证请求时应用不同的约束: 身份验证 - 在授权之前;它是关于验证收到的凭证;我们验证用户名和密码是否与我们的应用程序识别的用户

  • Spring security实现登陆和权限角色控制

     随笔简介 1.spring版本:4.3.2.RELEASE+spring security 版本:4.1.2.RELEASE(其它不做说明) 2.所展示内容全部用注解配置 3.springmvc已经配置好,不作说明 4.会涉及到springmvc,spel,el的东西,不熟悉的同学可以先去看一下这方面内容,特别是springmvc 首先想一下,登陆需要什么,最简单的情况下,用户名,密码,然后比对数据库,如果吻合就跳转到个人页面,否则回到登陆页面,并且提示用户名密码错误.这个过程中应该还带有权限

  • Vue 动态路由的实现及 Springsecurity 按钮级别的权限控制

    思路 : 动态路由实现:在导航守卫中判断用户是否有用户信息, 通过调用接口,拿到后台根据用户角色生成的菜单树, 格式化菜单树结构信息并递归生成层级路由表并 使用Vuex保存,通过  router.addRoutes  动态挂载到  router  上,按钮级别的权限控制,则需使用自定义指令去实现. 实现: 导航守卫代码: router.beforeEach((to, from, next) => { NProgress.start() // start progress bar to.meta

  • 浅谈Spring Security 对于静态资源的拦截与放行

    初始创建Spring Boot项目,使用thymeleaf作为模板引擎,利用Spring Security进行验证管理,根据官方例子试验成功(官方的Spring Security示例). 然后准备整合页面直接将html甩到templates目录下,静态资源甩到static目录下. 简单的测试页面,发现会报错如下: Refused to apply style from 'http://localhost:8080/login' because its MIME type ('text/html'

  • 使用Spring Security OAuth2实现单点登录

    1.概述 在本教程中,我们将讨论如何使用Spring Security OAuth和Spring Boot实现SSO - 单点登录. 我们将使用三个单独的应用程序: •授权服务器 - 这是中央身份验证机制 •两个客户端应用程序:使用SSO的应用程序 非常简单地说,当用户试图访问客户端应用程序中的安全页面时,他们将被重定向到首先通过身份验证服务器进行身份验证. 我们将使用OAuth2中的授权代码授权类型来驱动身份验证委派. 2.客户端应用程序 让我们从客户端应用程序开始;当然,我们将使用Sprin

  • SpringBoot2.0 整合 SpringSecurity 框架实现用户权限安全管理方法

    一.Security简介 1.基础概念 Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring的IOC,DI,AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为安全控制编写大量重复代码的工作. 2.核心API解读 1).SecurityContextHolder 最基本的对象,保存着当前会话用户认证,权限,鉴权等核心数据.Secu

  • 基于Spring Security的Oauth2授权实现方法

    前言 经过一段时间的学习Oauth2,在网上也借鉴学习了一些大牛的经验,推荐在学习的过程中多看几遍阮一峰的<理解OAuth 2.0>,经过对Oauth2的多种方式的实现,个人推荐Spring Security和Oauth2的实现是相对优雅的,理由如下: 1.相对于直接实现Oauth2,减少了很多代码量,也就减少的查找问题的成本. 2.通过调整配置文件,灵活配置Oauth相关配置. 3.通过结合路由组件(如zuul),更好的实现微服务权限控制扩展. Oauth2概述 oauth2根据使用场景不同

  • Spring Security OAuth2认证授权示例详解

    本文介绍了如何使用Spring Security OAuth2构建一个授权服务器来验证用户身份以提供access_token,并使用这个access_token来从资源服务器请求数据. 1.概述 OAuth2是一种授权方法,用于通过HTTP协议提供对受保护资源的访问.首先,OAuth2使第三方应用程序能够获得对HTTP服务的有限访问权限,然后通过资源所有者和HTTP服务之间的批准交互来让第三方应用程序代表资源所有者获取访问权限. 1.1 角色 OAuth定义了四个角色 资源所有者 - 应用程序的

  • Spring Security角色继承分析

    今天想和小伙伴们来聊一聊 Spring Security 中的角色继承问题. 角色继承实际上是一个很常见的需求,因为大部分公司治理可能都是金字塔形的,上司可能具备下属的部分甚至所有权限,这一现实场景,反映到我们的代码中,就是角色继承了. Spring Security 中为开发者提供了相关的角色继承解决方案,但是这一解决方案在最近的 Spring Security 版本变迁中,使用方法有所变化.今天除了和小伙伴们分享角色继承外,也来顺便说说这种变化,避免小伙伴们踩坑,同时购买了我的书的小伙伴也需

  • Spring Security角色继承实现过程解析

    在SecurityConfig中加入 @Bean RoleHierarchy roleHierarchy(){ RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl(); String hierarchy ="ROLE_adb > ROLE_admin\n ROLE_admin > ROLE_user"; roleHierarchy.setHierarchy(hierarchy); return roleHierar

  • Spring Security 在 Spring Boot 中的使用详解【集中式】

    1.1 准备 1.1.1 创建 Spring Boot 项目   创建好一个空的 Spring Boot 项目之后,写一个 controller 验证此时是可以直接访问到该控制器的. 1.1.2 引入 Spring Security   在 Spring Boot 中引入 Spring Security 是相当简单的,可以在用脚手架创建项目的时候勾选,也可以创建完毕后在 pom 文件中加入相关依赖. <dependency> <groupId>org.springframework

  • Spring Security 安全框架应用原理解析

    Spring Security 简介 背景分析 企业中数据是最重要的资源,对于这些数据而言,有些可以直接匿名访问,有些只能登录以后才能访问,还有一些你登录成功以后,权限不够也不能访问.总之这些规则都是保护系统资源不被破坏的一种手段.几乎每个系统中都需要这样的措施对数据(资源)进行保护.我们通常会通过软件技术对这样业务进行具体的设计和实现.早期没有统一的标准,每个系统都有自己独立的设计实现,但是对于这个业务又是一个共性,后续市场上就基于共享做了具体的落地实现,例如Spring Security,A

  • Spring Security 中如何让上级拥有下级的所有权限(案例分析)

    答案是能! 松哥之前写过类似的文章,但是主要是讲了用法,今天我们来看看原理! 本文基于当前 Spring Security 5.3.4 来分析,为什么要强调最新版呢?因为在在 5.0.11 版中,角色继承配置和现在不一样.旧版的方案我们现在不讨论了,直接来看当前最新版是怎么处理的. 1.角色继承案例 我们先来一个简单的权限案例. 创建一个 Spring Boot 项目,添加 Spring Security 依赖,并创建两个测试用户,如下: @Override protected void con

  • SpringBoot工程中Spring Security应用实践记录流程分析

    目录 SpringSecurity 应用 简介 认证授权分析 SpringSecurity 架构设计 快速入门实践 创建项目 添加项目依赖 启动服务访问测试 自定义认证逻辑 认证流程分析 定义security配置类 定义数据访问层对象 定义UserDetailService接口实现类 自定义登陆页面 启动服务进行访问测试 授权逻辑设计及实现 修改授权配置类 定义资源访问对象 启动服务实现访问测试 总结(Summary) SpringSecurity 应用 简介 Spring Security是一

  • SpringBoot整合Spring Security过滤器链加载执行流程源码分析(最新推荐)

    目录 1.引言 2.Spring Security过滤器链加载 2.1.注册名为 springSecurityFilterChain的过滤器 3.查看 DelegatingFilterProxy类 4.查看 FilterChainProxy类 4.1 查看 doFilterInternal方法 4.2 查看 getFilters方法 5 查看 SecurityFilterChain接口 6. 查看 SpringBootWebSecurityConfiguration类 总结: 1.引言 在 Sp

  • Spring Security中的Servlet过滤器体系代码分析

    1. 前言 我在Spring Security 实战干货:内置 Filter 全解析对Spring Security的内置过滤器进行了罗列,但是Spring Security真正的过滤器体系才是我们了解它是如何进行"认证"."授权"."防止利用漏洞"的关键. 2. Servlet Filter体系 这里我们以Servlet Web为讨论目标,Reactive Web暂不讨论.我们先来看下最基础的Servlet体系,在Servlet体系中客户端发起

  • Spring security基于数据库中账户密码认证

    一.原理分析 前台的登录请求发送到后端后会由spring security进行拦截,即controller层由框架自己提供.这样用户名和密码的认证就需要在service层完成,所以框架需要在service层获取到我们自己的数据库账号信息. spring security 提供了一个接口 UserDetailsService 来让用户提供账号和密码,其内容如下 public interface UserDetailsService { UserDetails loadUserByUsername(

随机推荐