spring – 自定义http安全配置以及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")...;
    }

}

资源服务器:

@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);
    }
}

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

@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();
    }
}

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

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

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

最佳答案
假设你想使用oauth2,并且首先得到一个令牌并在你的API中使用它(使用Authorization Bearer [TOKEN]),你需要更改你的逻辑以创建自定义表达式.

首先,如果您使用spring boot 1.5,请考虑将以下属性添加到您的application.properties文件中:security.oauth2.resource.filter-order = 3(1.5 release notes)

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

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

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

首先,我们需要创建一个自定义的MethodSecurityExpressionOperations.在它的底部你可以找到//自定义逻辑方法,说明自定义逻辑的开始(在它之上是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 */
    }

接下来,我们需要在自定义MethodSecurityExpressionHandler中将其设置为root:

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;
    }

}

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

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

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

    @Bean
    public MethodSecurityExpressionHandler methodSecurityExpressionHandler() {
        return new CustomOAuth2MethodSecurityExpressionHandler();
    }

现在在您的控制器中,您可以添加@PreAuthorize(“securityHasPermission(‘SOME’,’VALUE’)”)以触发您的自定义逻辑

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

转载注明原文:spring – 自定义http安全配置以及OAuth2资源服务器 - 代码日志