【问题标题】:Spring Security OAuth2: '#oauth2.xxx' expressions not evaluted with multiple RequestMatchers mapped to the same FilterChainSpring Security OAuth2:“#oauth2.xxx”表达式未使用映射到同一 FilterChain 的多个 RequestMatchers 进行评估
【发布时间】:2019-05-30 13:12:12
【问题描述】:

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

@Configuration
public class Module1SecurityFilterChain extends ResourceServerConfigurerAdapter {

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

模块2:

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

并启用方法安全性:

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

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

问题是所有 #oauth2.xx 表达式仅针对第一个模块 requestmatcher /module1/** 进行评估,而在其他模块中被忽略。当我对用户进行身份验证并尝试访问/module1/test 时,访问被拒绝,而当访问/module2/test 时,访问被授予(也应该被拒绝)。

谁能解释一下为什么以及如何解决这个问题?我知道 Spring Security 一点也不简单…… 再次感谢。

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

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]

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

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

我不明白的是,为什么对于其他模块,#oauth2.xx 表达式没有被评估,因为 FilterChain 是相同的?

【问题讨论】:

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


【解决方案1】:

请求匹配器(在antMatchers、anyRequest等中指定)由过滤器链按照它们指定的顺序处理。因为多个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 编码的令牌是这样。这也很好,因为两个WebSecurityConfigurerAdapters 被视为单独的过滤器链。

根据您需要的 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;因此,请务必与您的特定用例联系,以确保它在尚未到位时得到优先考虑。

【讨论】:

  • 感谢您提供这些详细信息,我将 Spring Security 5.1.2 和 2.3.4 用于 OAuth2。您有权在 Spring Security Core 中合并 OAuth2 的配置部分,因为拥有 2 个配置太麻烦了。我经常不知道ResourceServerConfigurerAdapter 和WebSecurityConfigurerAdapter 之间哪个优先。我绝对不知道oauth2ResourceServer() 方法。在所有情况下,Spring Security 都将是我最糟糕的噩梦 ^^,我希望完全理解它是如何工作的。再次感谢
  • @akuma8 根据您需要的 OAuth 2.0 功能,您不需要包含 OAuth2 2.3.4(包含 ResourceServerConfigurerAdapter 的依赖项),请参阅 github.com/spring-projects/spring-security/wiki/…。 Spring Security 5.1 原生包含 OAuth2,并在每个版本中引入新的 OAuth 2.0 特性(5.2 即将到来)。关于您的挫败感,您希望如何看待 Spring Security 的变化?我会邀请您根据您的建议开票:github.com/spring-projects/spring-security/issues/new
猜你喜欢
  • 1970-01-01
  • 2015-01-17
  • 2014-09-15
  • 2013-03-28
  • 1970-01-01
  • 2019-09-25
  • 2018-07-23
  • 2019-04-29
  • 2019-04-06
相关资源
最近更新 更多