在 Spring Security (Spring Boot 2.x) 中,我如何为 @Pre/PostAuthorize 和检查角色提供我自己的实现?

In Spring Security (Spring Boot 2.x) how do I provide my own implementation for @Pre/PostAuthorize and checking roles?

我想使用内置的 Spring @PreAuthorize 注释和 hasRolehasAnyRole 等,但有 Spring class es 调用我的实现来确定它是否应该是 true/false。我该怎么做?

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

我需要实施 SecurityExpressionRoot class 吗?如果是这样,我应该在哪里告诉它使用我的?

我尝试覆盖访问决策管理器并添加我自己的选民,但即使它调用了我的方法并且我 return true(并且它是一个 AffirmativeBased 管理器),它仍然去 SecurityExpressionRoot.hasAnyRole() 然后 returns 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";
}

可能有帮助:

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 方法中的参数。你应该提供 bean of AuthorizationService.

如果你想让 @PreAuthorize("hasRole('USER')") 调用你的方法而不是 SecurityExpressionRoot 中的方法,那么,是的,你需要通过将你自己的 MethodSecurityExpressionHandler 公开为一个豆子。您将覆盖其 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 确定访问权限来做任何您需要做的事情。

制图机构

或者,您可以考虑将自定义角色中的任何内容映射到一组 GrantedAuthority 中。 Spring 安全性中的一些身份验证机制带有映射自定义权限表示的方法。

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