自定义http安全配置以及OAuth2资源服务器

Mil*_*nov 5 spring spring-security spring-boot spring-security-oauth2

我正在使用Spring Security OAuth2一个非常基本的配置,工作正常.我现在想要一个WebSecurityConfigurerAdapter包含自定义逻辑的独立逻辑,该逻辑确定某人是否有权访问某些端点.但是,无论我尝试什么,都不会执行.以下是我的OAuth2配置和我对该主题的调查结果.授权服务器:

@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private AuthenticationManagerBuilder authenticationManager;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints)throws Exception {
        endpoints.authenticationManager(authentication -> authenticationManager.getOrBuild().authenticate(authentication)).tokenStore(tokenStore);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory().withClient("CLIENT_NAME")...;
    }

}
Run Code Online (Sandbox Code Playgroud)

资源服务器:

@Configuration
@EnableResourceServer
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Autowired
    private TokenStore tokenStore;

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated();
    }

    @Override
    public void configure(final ResourceServerSecurityConfigurer resources) throws Exception {
        resources.tokenStore(tokenStore);
    }
}
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.当自定义WebSecurityConfigurerAdapter生效时,我开始遇到问题.由于EnableResourceServer-annotated bean创建了一个WebSecurityConfigurerAdapterwith,Order(3)它首先在每个请求上执行,用户被成功验证/授权,但我的自定义逻辑WebSecurityConfiguration没有被执行.另一方面,如果我设置WebSecurityConfigurationOrder(2)或者更少,access则执行自定义规则,但它总是说它们来自匿名用户(因为创建的bean中的规则@EnableResourceServer未被执行).

@EnableWebSecurity
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Bean
    public TokenStore tokenStore() {
        return new InMemoryTokenStore();
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/...");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();

        http.authorizeRequests().antMatchers(HttpMethod.GET, "/path/**")
        .access("@security.hasPermission(authentication, 'SOME', 'VALUE')");

        http.authorizeRequests().anyRequest().authenticated();
    }
}
Run Code Online (Sandbox Code Playgroud)

正如旁注,规则中的@security引用access只是一个简单的命名Spring bean:

@Component("security")
public class SecurityService {
    public boolean hasPermission(Authentication authentication, String param, String anotherParam) { ... }
}
Run Code Online (Sandbox Code Playgroud)

我有集成测试验证自定义访问规则WebSecurityConfiguration并且它们有效(因为我在那里跳过了身份验证).我希望能够仅使用资源服务器进行身份验证,然后使用我的自定义http安全性进行授权.

Tom*_*Tom 2

假设您想使用 oauth2,并且首先获得一个令牌并在 API 中使用它(使用Authorization Bearer [TOKEN]),您需要稍微更改一下逻辑才能创建自定义表达式。

首先,如果您使用 spring boot 1.5+,请考虑将以下属性添加到您的 application.properties 文件中:security.oauth2.resource.filter-order=31.5 发行说明

现在,为了创建自定义过滤器,您需要了解一些组件。

默认情况下,spring 提供以下接口MethodSecurityExpressionHandler来处理安全表达式,使用的实现是DefaultMethodSecurityExpressionHandler. 通常您会使用 oauth2 覆盖它OAuth2MethodSecurityExpressionHandler,但让我们使用我们自己的实现创建一个自定义的。

此类使用MethodSecurityExpressionOperations实现的接口来SecurityExpressionRoot解析表达式。

首先,我们需要创建一个自定义的MethodSecurityExpressionOperations. 在它的底部,你可以找到// custom logic methods自定义逻辑的开始(在它的顶部是 spring 的实现,以保持与其表达式的兼容性):

public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {

    // Based on MethodSecurityExpressionRoot (class is package private in spring)
    private Object filterObject;
    private Object returnObject;
    private Object target;

    CustomMethodSecurityExpressionRoot(Authentication a) {
        super(a);
    }

    public void setFilterObject(Object filterObject) {
        this.filterObject = filterObject;
    }

    public Object getFilterObject() {
        return filterObject;
    }

    public void setReturnObject(Object returnObject) {
        this.returnObject = returnObject;
    }

    public Object getReturnObject() {
        return returnObject;
    }

    /**
     * Sets the "this" property for use in expressions. Typically this will be
     * the "this" property of the {@code JoinPoint} representing the method
     * invocation which is being protected.
     *
     * @param target
     *            the target object on which the method in is being invoked.
     */
    void setThis(Object target) {
        this.target = target;
    }

    public Object getThis() {
        return target;
    }

    // custom logic methods
    public boolean securityHasPermission(String param, String anotherParam) {
        /* custom logic here */
    }
Run Code Online (Sandbox Code Playgroud)

接下来,我们需要将其设置为自定义中的根MethodSecurityExpressionHandler

public class CustomOAuth2MethodSecurityExpressionHandler extends OAuth2MethodSecurityExpressionHandler {

    private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();

    @Override
    protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
            MethodInvocation invocation) {
        final CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(authentication);
        root.setThis(invocation.getThis());
        root.setPermissionEvaluator(getPermissionEvaluator());
        root.setTrustResolver(this.trustResolver);
        root.setRoleHierarchy(getRoleHierarchy());

        return root;
    }

}
Run Code Online (Sandbox Code Playgroud)

接下来,我们需要将其设置为默认值MethodSecurityExpressionHandler。我们可以通过扩展来做到这一点GlobalMethodSecurityConfiguration。在该文件中,我们@Bean为自定义MethodSecurityExpressionHandler和覆盖定义了一个新的createExpressionHandler(),以便将其设置为默认值:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class Oauth2GlobalMethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {

    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        return methodSecurityExpressionHandler();
    }

    @Bean
    public MethodSecurityExpressionHandler methodSecurityExpressionHandler() {
        return new CustomOAuth2MethodSecurityExpressionHandler();
    }
Run Code Online (Sandbox Code Playgroud)

现在,您可以在控制器中添加@PreAuthorize("securityHasPermission('SOME', 'VALUE')")以触发自定义逻辑

您的自定义逻辑应该被触发,CustomMethodSecurityExpressionRoot并且您可以访问authentication变量,因为它在初始化时已经注入到类中。您还可以在设置时传递更多参数/bean CustomOAuth2MethodSecurityExpressionHandler