【问题标题】:In Spring Security (Spring Boot 2.x) how do I provide my own implementation for @Pre/PostAuthorize and checking roles?在 Spring Security (Spring Boot 2.x) 中,我如何为 @Pre/PostAuthorize 和检查角色提供自己的实现?
【发布时间】:2019-05-21 08:01:39
【问题描述】:

我想使用内置的 Spring @PreAuthorize 注释和 hasRolehasAnyRole 等,但是让 Spring 类调用我的实现来确定它是否应该为真/假。我该怎么做?

WebSecurityConfigurerAdapter 中是否有我可以覆盖的配置?

我需要实现SecurityExpressionRoot 类吗?如果是这样,我在哪里告诉它使用我的?

我尝试覆盖访问决策管理器并添加我自己的选民,但即使它调用我的方法并且我返回 true(它是一个 AffirmativeBased 管理器),它仍然转到 SecurityExpressionRoot.hasAnyRole() 然后返回 false .

public class MyDecisionVoter implements AccessDecisionVoter<Object>
{
    @Override
    public boolean supports(ConfigAttribute attribute)
    {
        //We want to always be called
        return true;
    }


    @Override
    public boolean supports(Class<?> clazz)
    {
        //We want to always be called
        return true;
    }

    @Override
    public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes)
    {
        //For testing purposes
        return ACCESS_GRANTED;
    }
}

经理

public class MyAffirmativeBasedDecisionManager extends AffirmativeBased
{
    public MyAffirmativeBasedDecisionManager(List<AccessDecisionVoter<?>> decisionVoters)
    {
        super( decisionVoters );
    }

    @Override
    public boolean supports(Class<?> clazz)
    {
        for ( AccessDecisionVoter<?> voter : this.getDecisionVoters() )
        {
            if ( voter.supports( clazz ) )
            {
                return true;
            }
        }

        return false;
    }
}

配置

@EnableWebSecurity
@EnableGlobalMethodSecurity( prePostEnabled = true )
public class MyConfig extends WebSecurityConfigurerAdapter
{
    @Override
    protected void configure(HttpSecurity http) throws Exception
    {
        //Turn on OAuth
        http.authorizeRequests()
            .anyRequest()
            .authenticated()
            .accessDecisionManager( createDecisionManager() );
    }

    private AccessDecisionManager createDecisionManager()
    {
        List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList<>();

        ExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice();
        expressionAdvice.setExpressionHandler( new DefaultMethodSecurityExpressionHandler() );

        decisionVoters.add( new MyDecisionVoter() );
        decisionVoters.add( new PreInvocationAuthorizationAdviceVoter( expressionAdvice ) );
        decisionVoters.add( new RoleVoter() );
        decisionVoters.add( new AuthenticatedVoter() );

        return new MyAffirmativeBasedDecisionManager( decisionVoters );
    }
}

这应该让他们进来,但它会失败并出现 403:

@GetMapping( "shouldallow" )
@ResponseBody
@PreAuthorize( "hasRole('ROLE_NOT_EXIST')" )
public String shouldAllow()
{
    return "should allow";
}

【问题讨论】:

    标签: java spring spring-boot spring-security roles


    【解决方案1】:

    如果你想让@PreAuthorize("hasRole('USER')") 调用你的方法而不是SecurityExpressionRoot 中的方法,那么,是的,你需要通过将你自己的MethodSecurityExpressionHandler 暴露为一个bean 来替换它。你会覆盖它的createSecurityExpressionRoot 方法:

    class MyExpressionHandler extends DefaultMethodSecurityExpressionHandler {
        @Override
        protected MethodSecurityExpressionOperations
            createSecurityExpressionRoot(Authentication a, MethodInvocation mi) {
            return new MyRoot(super.createSecurityExpressionRoot(a, mi));
        }
    }
    
    @EnableGlobalMethodSecurity(prePostEnabled=true)
    class UsingCustomExpressionHandler extends GlobalMethodSecurityConfiguration {
        @Override
        protected MethodSecurityExpressionHandler createExpressionHandler() {
            return new MyExpressionHandler();
        }
    }
    

    但是,您可以先尝试一些侵入性较小的东西。

    使用 Bean

    例如,您可以refer to any of your own beans inside a SpEL。因此,如果您创建了一个可以执行评估的@Bean,那么您不需要调用hasRole。相反,您可以这样做:

    @PreAuthorize("@myBean.evaluate(authentication)")
    

    它为您提供了很大的灵活性,您可以使用Authentication 来确定访问权限。

    测绘机构

    或者,您可以考虑将您拥有的任何自定义角色映射到一组GrantedAuthoritys。 Spring Security 中的一些身份验证机制附带了一种映射自定义权限表示的方法。

    例如,我注意到您的评论// turn on OAuth。如果由于 OAuth 范围而想要覆盖 hasRole,则可以使用 oauth2ResourceServer()supply a custom JwtAuthenticationConverter 将自定义权限调整为 GrantedAuthoritys。在这种情况下,hasRole 可能根本不需要被覆盖。 (当然,我不知道你的具体情况是如何认证用户的。这只是GrantedAuthority 转换的一个例子。)

    【讨论】:

    • 我将此标记为答案,因为它是一种解决方案,但实际上我通过定义 AccessDecisionManagerAccessDecisionVoter 解决了这个问题
    【解决方案2】:

    这可能会有所帮助:

    public class AuthorizationService {
    
    public boolean hasAccess(Object obj) {
     // your code here
    }
    }
    
    @GetMapping( "/someurl" )
    @PreAuthorize("@authorizationService.hasAccess(#obj)")
    public void dummyMethod(@PathVariable("obj") Object obj) {
    }
    

    如果你根据某个对象定义权限,你可以直接将它传递给方法。否则你可以忽略hasAccess 方法中的参数。您应该提供beanAuthorizationService

    【讨论】:

    • 当有人使用 hasRole 时,这有什么帮助?
    • @DonRhummy 只是将 hasAccess 更改为 hasRole,概念上它们是相同的
    猜你喜欢
    • 1970-01-01
    • 2016-10-03
    • 2014-12-26
    • 2018-09-03
    • 2014-05-01
    • 2018-07-28
    • 2021-12-21
    • 2020-12-31
    • 2019-03-07
    相关资源
    最近更新 更多