Spring Security OAuth2:'#oauth2.xxx'表达式未使用映射到同一FilterChain的多个RequestMatchers进行求值

时间:2019-01-03 22:50:31

标签: spring spring-boot spring-security spring-security-oauth2

在多模块应用中,我定义了5个映射到同一FilterChain的RequestMatchers,如下所示:

@Configuration
public class Module2SecurityFilterChain extends ResourceServerConfigurerAdapter {

   @Override
   public void configure( HttpSecurity http ) throws Exception {
      http.sessionManagement().sessionCreationPolicy( STATELESS );
      http.requestMatchers().antMatchers( "/module2/**")
            .and()
            .authorizeRequests()
            .antMatchers( "/module2/resource").authenticated()
            .antMatchers( "/module2/test" ).access( "#oauth2.isClient()")
            .anyRequest().access( "#oauth2.hasScope('webclient')" );
   }
}

和模块2:

@Configuration
@EnableGlobalMethodSecurity( prePostEnabled = true, securedEnabled = true )
public class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {

    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        return new OAuth2MethodSecurityExpressionHandler();
    }
}

并启用了方法安全性:

#oauth2.xx

问题在于,所有/module1/**表达式仅针对第一个模块requestmatcher /module1/test求值,而在其他表达式中被忽略。当我对用户进行身份验证并尝试访问/module2/test时,访问将按预期被拒绝,而当访问INFO | o.s.s.w.DefaultSecurityFilterChain | Creating filter chain: OrRequestMatcher [requestMatchers=[Ant [pattern='/oauth/token'], Ant [pattern='/oauth/token_key'], Ant [pattern='/oauth/check_token']]], [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@f55a810b, org.springframework.security.web.context.SecurityContextPersistenceFilter@85021903, org.springframework.security.web.header.HeaderWriterFilter@1d0744d1, org.springframework.security.web.authentication.logout.LogoutFilter@2d15146a, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@c38f3266, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@8f9bf85, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@74a71be5, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@e4eb6cc, org.springframework.security.web.session.SessionManagementFilter@22f6b39a, org.springframework.security.web.access.ExceptionTranslationFilter@960c464f, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@f7a19dc5] INFO | o.s.s.w.DefaultSecurityFilterChain | Creating filter chain: OrRequestMatcher [requestMatchers=[Ant [pattern='/module1/**'], Ant [pattern='/module2/**'], Ant [pattern='/module3/**'], Ant [pattern='/module4/**'], Ant [pattern='/module5/**'], Ant [pattern='/module6/**']]], [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@38ef2427, org.springframework.security.web.context.SecurityContextPersistenceFilter@a26ff7af, org.springframework.security.web.header.HeaderWriterFilter@5344e710, org.springframework.security.web.authentication.logout.LogoutFilter@da0534c8, org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter@2956c7ab, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@5682f610, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@f4cbf7a4, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@d1b1395a, org.springframework.security.web.session.SessionManagementFilter@d352f8ab, org.springframework.security.web.access.ExceptionTranslationFilter@9bb1d86, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@73c7a695] INFO | o.s.s.w.DefaultSecurityFilterChain | Creating filter chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@1cc2056f, org.springframework.security.web.context.SecurityContextPersistenceFilter@259d95db, org.springframework.security.web.header.HeaderWriterFilter@de089e0b, org.springframework.security.web.authentication.logout.LogoutFilter@8b86b4c, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@96304ca8, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@1d5b7e4b, org.springframework.security.web.session.SessionManagementFilter@bd586b4d, org.springframework.security.web.access.ExceptionTranslationFilter@7cff2571] 时,访问被授予(也应被拒绝)。

有人可以解释我为什么以及如何解决这个问题吗?我知道Spring Security并不容易... 再次感谢。

编辑 @Darren Forsythe(感谢您的评论) 创建的过滤器链为:

Security filter chain: [
  WebAsyncManagerIntegrationFilter
  SecurityContextPersistenceFilter
  HeaderWriterFilter
  LogoutFilter
  OAuth2AuthenticationProcessingFilter
  RequestCacheAwareFilter
  SecurityContextHolderAwareRequestFilter
  AnonymousAuthenticationFilter
  SessionManagementFilter
  ExceptionTranslationFilter
  FilterSecurityInterceptor
]

如您所见,使用以下过滤器列表,所有模块的URL都映射到同一过滤器链:

#oauth2.xx

我不明白为什么对于其他模块,{{1}}表达式由于FilterChain相同而未求值?

1 个答案:

答案 0 :(得分:0)

请求匹配器(在antMatchersanyRequest等中指定)按指定的顺序由过滤器链处理。因为多个ResourceServiceConfiguredAdapter实例只是配置了HttpSecurity的同一实例,所以将为您的每个请求按如下方式处理匹配器:

if (uri == "/module1/resource") {
    // ...
} else if (uri == "/module1/test") { 
    // ...
} else if (true) { // anyRequest
    // ...
} else if (uri = "/module2/resource") {
    // ...
} else if (uri = "/module2/test") {
    // ...
}

如您所见,最后两个if条件永远不会满足。

您可以考虑以下两点:

替换anyRequest()

anyRequest通常非常方便;但是,在这种情况下,由于试图将范围缩小到某些模块路径,因此您实际上并不是“任何请求”。您可以改为:

http
    .requestMatchers()
        .antMatchers("/module2/**")
        .and()
    .authorizeRequests()
        .antMatchers("/module2/resource").authenticated()
        .antMatchers("/module2/test").access( "#oauth2.isClient()")
        .antMatchers("/module2/**").access( "#oauth2.hasScope('webclient')" );

这样,该模块就不会过度尝试尝试指定可能不知道的行为。

说实话,调用anyRequest通常是无害的,因为您已经使用requestMatchers缩小了过滤器链的范围。但是,由于您是用多个适配器组成一个HttpSecurity,所以存在这种隐藏的复杂性。

oauth2ResourceServer()-Spring Security 5.1 +

如果您使用的是Spring Security 5.1,那么实际上WebSecurityConfigurerAdapter本身就内置了支持,因此您不再需要使用ResourceServerConfigurerAdapter了,至少对于以下位置的JWT编码令牌而言:这点。这也很好,因为两个WebSecurityConfigurerAdapter被视为单独的过滤器链。

根据所需的OAuth 2.0功能,您可以改为执行此操作:

@EnableWebSecurity
public class Module1Config extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .requestMatchers()
                .antMatchers("/module1/**")
                .and()
            .authorizeRequests()
                .antMatchers("/module1/resource").authenticated()
                .anyRequest.hasRole("SCOPE_webclient")
            .oauth2ResourceServer()
                .jwt();
    }
}

这是Spring Security的一个活跃的开发领域-将功能移植到WebSecurityConfigurerAdapter中;因此,请务必与您的特定用例联系,以确保在尚未使用的情况下对其进行优先级排序。