SpringSecurity6.0.1中角色继承无效的问题

文章讲述了在使用SpringSecurity框架时遇到角色继承不起作用的问题,详细分析了通常的做法,即自定义RoleHierarchyBean,以及在实际测试中发现的问题。作者通过调试发现,问题在于6.0.1版本未完全支持RoleHierarchyBean配置。为了解决这个问题,作者参考SpringSecurity的issue,提供了新的配置方式,通过DefaultMethodSecurityExpressionHandler来设置RoleHierarchy,最终实现了角色继承的正确工作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、简述

  我在 SpringSecurity 中做了不少的实验,但总感觉对这个框架还是有点模糊,最近在复习 SpringSecurity,进行到角色继承时,发现角色继承并没有起作用,折腾了很久最后在 SpringSecurity 的 issue 中找到了解决方法,主要问题就是 6.0.1 的版本还没有很好地兼容 RoleHierarchy 这个 Bean。

二、通常做法

  通常我们做角色继承只需要自定义注入一个 RoleHierarchy 的 Bean 就可以了,就像这样:

@Bean
    static RoleHierarchy roleHierarchy() {
        RoleHierarchyImpl hierarchy = new RoleHierarchyImpl();
        hierarchy.setHierarchy("ROLE_ADMIN > ROLE_user");
        return hierarchy;
    }

三、试验

  准备三个接口,其中 getAll 不设置访问权限,getUser 只允许有 User 权限的用户访问,getAdmin 只允许有 Admin 权限的用户访问。

    @RequestMapping("/getAll")
    public String getAll(@RequestParam("msg") String msg) {
        return msg + " ALL";
    }

    @RequestMapping("/getUser")
    @PreAuthorize("hasRole('user')")
    public String getUser(@RequestParam("msg") String msg) {
        return msg + " USER";
    }


    @RequestMapping("/getAdmin")
    @PreAuthorize("hasRole('ADMIN')")
    public String getAdmin(@RequestParam("msg") String msg) {
        return msg + " ADMIN";
    }

然后向登录接口发送请求,在这里用户 1111 的角色是管理员,111 则是普通用户,这两个账号是在保存再数据库中的,登录成功后返回用户名

在这里插入图片描述

之后访问 getAll

在这里插入图片描述

访问成功,再访问 getAdmin

在这里插入图片描述
也能访问到,最后再访问 getUser 看看角色继承是否有效

在这里插入图片描述

这里报 403 禁止访问了,为了探究是哪出的问题,我们在类 SecurityExpressionRoothasRole 方法中打个断点,之后一直步入到方法 hasAnyAuthorityName,该方法的第一行是将方法 getAuthoritySet 的结果放到 roleSet 中,根据名字这个方法应该是获取当前用户的角色集合。

在这里插入图片描述

步过该方法,发现这集合里面只有 ROLE*ADMIN

在这里插入图片描述

方法 hasAnyAuthorityName 后面的部分则是把 haseRole 中的角色加上前缀 ROLE*,并判断这个角色是否在 roleSet 里,那这肯定是不包括的,最后返回 False,所以就访问不到这个接口了。
我们重新在发送一次请求到 getUser,步入方法 getAuthoritySet

在这里插入图片描述

其中关键的第三行可以看到就是判断当前对象的 roleHierachy 是否为空,不为空则将返回所有可访问权限的集合

在这里插入图片描述

之后将权限集合转换为 Set

在这里插入图片描述

但在这里 roleHierachy 是 null 啊,what?我们配置的角色继承哪去了?

后面去翻了翻 SpringSecurity 的文档,它给的示例是这样的

@Bean
AccessDecisionVoter hierarchyVoter() {
    RoleHierarchy hierarchy = new RoleHierarchyImpl();
    hierarchy.setHierarchy("ROLE_ADMIN > ROLE_STAFF\n" +
            "ROLE_STAFF > ROLE_USER\n" +
            "ROLE_USER > ROLE_GUEST");
    return new RoleHierarchyVoter(hierarchy);
}

按照这个方法写了之后依旧还是不行。。。
idea 也提示类 AccessDecisionVoterRoleHierarchyVoter 已经弃用了,进入这两个类都提示改用 AuthorizationManager

在 SpringSecurity 的一个 issue 中发现有关文档中给的示例无效的问题
后面的回复中有一个解决方法。

Thanks for the report, @istoony. RoleHierarchy bean configuration is not fully ported over as of 6.0.x. As such, I think what should be done here is add a note about that in the documentation and then update it once completed. I’ve also added #12783 detailing what needs to be done to support RoleHierarchy bean configuration.
In the meantime, to configure RoleHierarchy for pre-post method security, use DefaultMethodSecurityExpressionHandler:

@Bean
static RoleHierarchy roleHierarchy() {
    RoleHierarchy hierarchy = new RoleHierarchyImpl();
    hierarchy.setHierarchy("ROLE_ADMIN > ROLE_STAFF\n" +
            "ROLE_STAFF > ROLE_USER\n" +
            "ROLE_USER > ROLE_GUEST");
    return new RoleHierarchyVoter(hierarchy);
}

@Bean
static DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler(RoleHierarchy roleHierarchy) {
    DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
    expressionHandler.setRoleHierarchy(roleHierarchy);
    return expressionHandler;
}

And to configure it for filter security, use the access(AuthorizationManager) method instead of hasRole, like so:

AuthorityAuthorizationManager<RequestAuthorizationContext> hasRoleUser =
    AuthorityAuthorizationManager.hasRole("USER");
hasRoleUser.setRoleHierarchy(roleHierarchy);

http
    .authorizeHttpRequests((authorize) -> authorize
        .requestMatchers("/needs/user/**").access(hasRoleUser)
        .anyRequest().authenticated()
    )
    // ...

按照这个示例将角色继承的配置修改为

@Bean
    static RoleHierarchy roleHierarchy() {
        RoleHierarchyImpl hierarchy = new RoleHierarchyImpl();
        hierarchy.setHierarchy("ROLE_ADMIN > ROLE_user");
        return hierarchy;
    }

    @Bean
    static DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler(RoleHierarchy roleHierarchy) {
        DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
        expressionHandler.setRoleHierarchy(roleHierarchy);
        return expressionHandler;
    }

经过测试,角色为 ADMIN 的用户都可以访问到 hasRole("user")的接口,并且 user 角色也还是访问不了 hasRole("ADMIN")的接口,角色继承生效了!

通常做法无效的原因

在 6.1.0-SNAPSHOT 版本的文档中对应角色继承的位置有这么一个提示

在这里插入图片描述

RoleHierarchy 这个 Bean 还没有适配@EnableMethodSecurity 这个注解,需要等到这个 issues 修完。

Spring Security 6.0.1Spring Security 的一个版本,它是一种基于 Spring 框架的安全性解决方案,用于保护 Web 应用程序和服务。Spring Security 提供了许多功能,如身份验证、授权、攻击防范、会话管理等,可以帮助开发人员轻松地为他们的应用程序添加安全性。 如果你想在你的项目中使用 Spring Security 6.0.1,你需要在你的项目中添加 Spring Security 的依赖。你可以在你的 pom.xml 文件中添加以下依赖: ``` <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>6.0.1</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>6.0.1</version> </dependency> ``` 这将会下载 Spring Security 6.0.1 的 JAR 文件并将其添加到你的项目中。接下来,你需要配置 Spring Security,以便它可以保护你的应用程序。你可以使用 Spring SecurityJava 配置或 XML 配置来完成这个任务。以下是一个简单的 Spring Security Java 配置示例: ``` @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .and() .httpBasic(); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() .withUser("user").password("password").roles("USER"); } } ``` 这个配置文件将会配置 Spring Security,以便它将所有请求都需要身份验证。它还配置了一个基本的表单登录和 HTTP 基本身份验证。最后,它添加了一个用户到内存中,以便该用户可以登录并访问应用程序。 我希望这可以回答你的问题。如果你有其他问题,请随时问我。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值