在 Spring Security 6 中实现动态权限管理

您所在的位置:网站首页 苹果手机获取权限管理 在 Spring Security 6 中实现动态权限管理

在 Spring Security 6 中实现动态权限管理

2024-07-14 01:03| 来源: 网络整理| 查看: 265

在 Spring Boot 3 之后,Spring Security 现在也升级到 Spring Security 6 了。

Spring Security 6 的用法跟之前比起来还是有很大差异,例如:动态权限定义的方式。

1、权限开发思路

先来说权限开发的思路,当设计好 RBAC 权限之后,具体到代码层面,有两种实现思路:

直接在接口/Service 层方法上添加权限注解,这样做的好处是实现简单,但是有一个问题就是权限硬编码,每一个方法需要什么权限都是代码中配置好的,后期如果想通过管理页面修改是不可能的,要修改某一个方法所需要的权限只能改代码。 将请求和权限的关系通过数据库来描述,每一个请求需要什么权限都在数据库中配置好,当请求到达的时候,动态查询,然后判断权限是否满足,这样做的好处是比较灵活,将来需要修改接口和权限之间的关系时,可以通过管理页面点击几下,问题就解决了,不用修改代码。

有人觉得第二种方案无法做到按钮级别的权限控制,这其实是一个误解。想要做到按钮级别的权限控制,只需要数据库中细化配置即可。

2、具体实践 2.1、旧方案回顾

在 vhr 项目中,通过重写两个类来和实现动态权限的。

第一个类是收集权限元数据的类:

@Component public class CustomFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource { @Override public Collection getAttributes(Object object) throws IllegalArgumentException { //... } @Override public Collection getAllConfigAttributes() { return null; } @Override public boolean supports(Class clazz) { return true; } }

在 getAttributes 方法中,根据当前请求的 URL 地址(从参数 Object 中可提取出来)和根据权限表中的配置,分析出来当前请求需要哪些权限并返回。

另外还重写了一个决策器。其实决策器也可以不重写,就看你自己的需求,如果 Spring Security 自带的决策器无法满足你的需求,那么可以自己写一个决策器:

@Component public class CustomUrlDecisionManager implements AccessDecisionManager { @Override public void decide(Authentication authentication, Object object, Collection configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { //... } @Override public boolean supports(ConfigAttribute attribute) { return true; } @Override public boolean supports(Class clazz) { return true; } }

decide 方法就是做决策的地方,第一个参数中可以提取出当前用户具备什么权限,第三个参数是当前请求需要什么权限,比较一下就行了,如果当前用户不具备需要的权限,则直接抛出 AccessDeniedException 异常即可。

最后,通过 Bean 的后置处理器 BeanPostProcessor,将这两个配置类放到 Spring Security 的 FilterSecurityInterceptor 拦截器中:

@Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .withObjectPostProcessor(new ObjectPostProcessor() { @Override public O postProcess(O object) { object.setAccessDecisionManager(customUrlDecisionManager); object.setSecurityMetadataSource(customFilterInvocationSecurityMetadataSource); return object; } }) .and() //... }

大致上的逻辑就是如此,以上类的完整代码可以在 https://github.com/lenve/vhr 找到。

2.2、新方案

不过以上代码在目前最新的 Spring Security 6 中用不了了。不是因为类过期了,而是因为类 被移除了!哪个类被移除了? FilterSecurityInterceptor!。

FilterSecurityInterceptor 这个过滤器以前是做权限处理的,但是在新版的 Spring Security6 中,这个拦截器被 AuthorizationFilter 代替了。

老实说,新版的方案其实更合理一些,传统的方案感觉带有很多前后端不分的影子,现在就往更纯粹的前后端分离发展。

由于新版中连 FilterSecurityInterceptor 都不用了,所以旧版的方案显然行不通了,新版的方案实际上更加简单。

虽然新旧写法不同,但是核心思路是一模一样。

来看下新版的配置:

@Bean SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests(register -> register.anyRequest().access((authentication, object) -> { //表示请求的 URL 地址和数据库的地址是否匹配上了 boolean isMatch = false; //获取当前请求的 URL 地址 String requestURI = object.getRequest().getRequestURI(); List menuWithRole = menuService.getMenuWithRole(); for (MenuWithRoleVO m : menuWithRole) { if (antPathMatcher.match(m.getUrl(), requestURI)) { isMatch = true; //说明找到了请求的地址了 //这就是当前请求需要的角色 List roles = m.getRoles(); //获取当前登录用户的角色 Collection


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3