Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using two or more SecurityFilterChains with Spring Security 6 does not work properly, only one chain is invoked [duplicate]

I have trouble to get my two SecurityFilterhains work in conjunction with each other using Spring Security 6. For one of my endpoint paths (/v1/transactions/**) I want the user to authorize with Oauth2 and for the other endpoint path (/v1/info) Basic Auth is required. Only one of the configurations works as expected depending on which @Order() they have.

With the below two SecurityFilterChain configurations I am able to make requests to /v1/info using Basic Auth but not making requests to /v1/transaction/** using Oauth2 which just gives me 401 Access Denied.

If I change the order so basicAuthSecurityFilterChain gets @Order(2) and oauth2SecurityFilterChain gets @Order(1) then I can make calls to /v1/transaction/** using OAauth2 but not make calls to /v1/info using Basic Auth which then gives me 401 Access Denied.

I am not sure why I encounter this behaviour since the docs tells me that the invocation of a SecurityFilterChain is decided based on the path and the paths for the basic auth and oauth2 resources are different (/v1/transaction/** vs /v1/info).

@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true)
@EnableWebSecurity
@Configuration
public class BasicAuthSecurity {
    
    public AuthenticationManager authProviderManager() { //omitted code) }

    @Bean
    @Order(1)
    public SecurityFilterChain basicAuthSecurityFilterChain(HttpSecurity http) throws Exception {
        return http
                .authenticationManager(authProviderManager())
                .authorizeHttpRequests(authorize -> authorize
                        .requestMatchers(POST, "/v1/info", "/v1/info/{user}").hasRole("user")
                        .anyRequest().authenticated()
                )
                .httpBasic(withDefaults())
                .build();
    }
}

And for the OAuth2 SecurityFilterChain I am using this:

@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true)
@EnableWebSecurity
@Configuration
public class Oauth2Security {

    @Bean
    @Order(2)
    public SecurityFilterChain oauth2SecurityFilterChain(HttpSecurity http) throws Exception {
        return http
                .authorizeHttpRequests(authorize -> authorize
                        .requestMatchers(POST, "/v1/transaction/**")
                        .hasAnyRole("poweruser", "admin")
                        .anyRequest().authenticated()
                )
                .oauth2ResourceServer()
                .jwt()
                .and().and().build();
    }
}
like image 541
Rox Avatar asked Jan 24 '26 08:01

Rox


1 Answers

you are missing securityMatcher in the first filter-chain in @Order. That simple :/

// Applies only to the specified security-matchers:
// requests with a Basic Authorization header 
@Bean
@Order(1)
public SecurityFilterChain basicAuthSecurityFilterChain(HttpSecurity http) throws Exception {
  http.securityMatcher((HttpServletRequest request) -> {
    return Optional.ofNullable(request.getHeader(HttpHeaders.AUTHORIZATION)).map(h -> {
      return h.toLowerCase().startsWith("basic ");
    }).orElse(false);
  });

  return http
      .authenticationManager(authProviderManager())
      .authorizeHttpRequests(authorize -> authorize
          .requestMatchers(POST, "/v1/info", "/v1/info/{user}").hasRole("user")
          .anyRequest().authenticated()
      )
      .httpBasic(withDefaults())
      .build();
}

// this one has lowest precedence (higher order) and no security matcher 
// => behaves as default when higher precedence (lower order) ones security matchers did not match 
@Bean
@Order(2)
public SecurityFilterChain oauth2SecurityFilterChain(HttpSecurity http) throws Exception {
  return http
      .authorizeHttpRequests(authorize -> authorize
          .requestMatchers(POST, "/v1/transaction/**")
          .hasAnyRole("poweruser", "admin")
          .anyRequest().authenticated()
      )
      .oauth2ResourceServer()
      .jwt()
      .and().and().build();
}

Note that you can write security-matchers using about anything from the requests. It is more frequent to see some matching path patterns:

http.securityMatcher("/v1/info/**");
like image 87
ch4mp Avatar answered Jan 27 '26 00:01

ch4mp



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!