Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
230 views
in Technique[技术] by (71.8m points)

Spring Security OAuth2: '#oauth2.xxx' expressions not evaluted with multiple RequestMatchers mapped to the same FilterChain

In a multi modules app I've defined 5 RequestMatchers mapped to the same FilterChain, like below:

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

And module2:

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

And enabled method security:

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

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

The problem is that all #oauth2.xx expressions are evaluated only for the 1st module requestmatcher /module1/** and ignored in others. When I authenticate a user and try to access to /module1/test the access is denied as expected whereas when accessing to /module2/test access is granted (it should also be denied).

Could someone explains me why and how to solve this? I know Spring Security isn't easy at all... Thanks again.

EDIT @Darren Forsythe (thanks for your comment) The filter chains created are:

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]

As you can see, all module's urls are mapped to the same filter chain with this list of filters:

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

What I don't understand is why for the other modules the #oauth2.xx expression is not evaluated since the FilterChain is the same?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Request matchers (specified in antMatchers, anyRequest, etc.) are processed by the filter chain in the order they are specified. Because multiple ResourceServiceConfiguredAdapter instances simply configure off of the same instance of HttpSecurity, the matchers are processed something like this for each one of your requests:

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

As you can see, the last two if conditions would never get hit.

Here are two things you can consider:

Replace anyRequest()

anyRequest is usually very handy; however, in this case, you don't actually mean "any request" since you are trying to narrow the scope to certain module paths. You might instead do:

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

That way, the module doesn't overreach and try and specify behavior that maybe it doesn't know about.

Truthfully, it is typically harmless to call anyRequest since you are already narrowing the scope of the filter chain already with requestMatchers. But, because you are composing a single HttpSecurity with multiple adapters, there is this hidden complexity.

oauth2ResourceServer() - Spring Security 5.1+

If you are on Spring Security 5.1, then there is actually support built into WebSecurityConfigurerAdapter natively, so you don't need to use ResourceServerConfigurerAdapter anymore, at least for JWT-encoded tokens at this point. This is also nice because two WebSecurityConfigurerAdapters are treated as separate filter chains.

Depending on the OAuth 2.0 features you need, you may be able to do that instead:

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

This is an active area of development for Spring Security right now - porting features over into WebSecurityConfigurerAdapter; so definitely reach out with your specific use case to make sure that it gets prioritized if it isn't already in place.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...