1

I'm building a Spring Boot v3.1.1 application using Spring Security as a dependency.

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.1.1'
    id 'io.spring.dependency-management' version '1.1.0'
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
}

I have created two custom filters expecting that they run after (last in the chain) Spring Security filters:

@Order(1)
@Component
public class DeviceFilter extends OncePerRequestFilter {}

@Order(2)
@Component
public class MdcFilter extends OncePerRequestFilter {}

They are working, but I cannot confirm that the order is correct.

I have tried this:

@Configuration
@EnableWebSecurity(debug = true)
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .formLogin(AbstractHttpConfigurer::disable)
                .httpBasic(AbstractHttpConfigurer::disable)
                .csrf(AbstractHttpConfigurer::disable) // Removes CsrfFilter
                .logout(AbstractHttpConfigurer::disable) // Removes LogoutFilter
                .rememberMe(AbstractHttpConfigurer::disable) // Removes RememberMeAuthenticationFilter
                .requestCache(RequestCacheConfigurer::disable) // Removes RequestCacheAwareFilter
                
                .cors(withDefaults()) // Adds CorsFilter

                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/public/**").permitAll()
                        .anyRequest().authenticated()
                )

                .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .oauth2ResourceServer(oauth2 -> oauth2.jwt(withDefaults()));

        return http.build();
    }

}

Result in the console:

Security filter chain: [
  DisableEncodeUrlFilter
  WebAsyncManagerIntegrationFilter
  SecurityContextHolderFilter
  HeaderWriterFilter
  CorsFilter
  BearerTokenAuthenticationFilter
  SecurityContextHolderAwareRequestFilter
  AnonymousAuthenticationFilter
  SessionManagementFilter
  ExceptionTranslationFilter
  AuthorizationFilter
]

If you can notice, my two custom filters are not there, but they are working somehow.

I think that Spring Security filters are independent of custom filters, and these are not added to the array of filters that they manage. I'm not sure, I'm not an expert on the subject.

Also, I have tried to put logs inside of my two custom filters to check the order. They are good.

09:05AM INFO DeviceFilter                     : {} DeviceFilter!!!!!!!
09:05AM INFO MdcFilter                        : {} MdcFilter!!!!!!!

But so far, I cannot guarantee that they are running after Spring Security filters. The reason is that I only want them to run if the user is successfully authenticated and has passed all the Spring Security checks.

Another thing that I have tried is to add my filters inside the securityFilterChain() method, this implies that my filters no longer implement @Order neither @Component:

@Configuration
@EnableWebSecurity(debug = true)
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                // .......
                // ......
                

                .addFilterAfter(new DeviceFilter(), AuthorizationFilter.class);
                .addFilterAfter(new MdcFilter(), DeviceFilter.class);

        return http.build();
    }

}

Result in the console:

Security filter chain: [
  DisableEncodeUrlFilter
  WebAsyncManagerIntegrationFilter
  SecurityContextHolderFilter
  HeaderWriterFilter
  CorsFilter
  BearerTokenAuthenticationFilter
  SecurityContextHolderAwareRequestFilter
  AnonymousAuthenticationFilter
  SessionManagementFilter
  ExceptionTranslationFilter
  AuthorizationFilter
  DeviceFilter // <=================
  MdcFilter // <=================
]

I would prefer to stick with the @Order and @Component approach because it's easier for me.

In other threads, they talk about how to set the order.

My question is how to confirm that the order I have (using @Order and @Component) is correct and they are running after Spring Security filters?

Thanks in advance and any push on this is appreciated.

RRGT19
  • 1,437
  • 3
  • 28
  • 54
  • in the `@Bean` annotated configuration function you are creating a `SecurityFilterChain`. and what you are printing in the console is that `SecurityFilterChain`. When annotating something with `@Component` you are registering your bean in the SpringContext, and not in the `SecurityFilterChain` so long story short, no i dont think you can do what is you are after, and what you are asking for makes honestly no sense just `because it's easier for me (you)`. What is easier? – Toerktumlare Jul 16 '23 at 20:22
  • @Toerktumlare Thanks for the explanation. So, there is a way to check if the filters in the SpringContext are running after SecurityFilterChain? About `because it's easier for me`, what I mean is that is easier to maintain and help me to keep all the configuration and responsibilities separated. If the filter is not for Security, why registering it under SecurityFilterChain. (is just a way of thinking). – RRGT19 Jul 16 '23 at 21:56
  • i dont really understand the "easier to maintain" argument, how one extra row be harder to maintain? how to check? enable logs? here you can read about "other" filters that are regiestered https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#web.servlet.embedded-container.servlets-filters-listeners.beans and what logging to enable – Toerktumlare Jul 16 '23 at 22:12
  • My code base is big and all my filters are within the same package. When a new filter comes in, I (or someone) may forgot to go to the SecurityConfig and add it. This step for me is a big mental issue, I would like to keep all about the filter, including the order, in the same class. Again, I think this is just a personal mental flow to avoid my head jumping around when setting a new filter. I will take a look at the website you have provided. Thanks a lot for all the help so far. – RRGT19 Jul 16 '23 at 23:27

1 Answers1

1

Your guess is correct . The filter used by the spring security (i.e FilterChainProxy) internally has its own filter chain (i.e SecurityFilterChain) which the order of those internal Filters will not be affected by @Order. The @Order only has effect on the standard servlet Filter beans including FilterChainProxy.

By analysing the source codes , ServletContextInitializerBeans is responsible for sorting all the standard servlet Filter beans based on @Order. Then after that , it will register the Filter to the servlet container one by one based on this sorting order.

So you can use it to indirectly confirm the Filter orders by defining a CommandLineRunner which will execute during application startup to print out the order. Something like :

@Bean
public CommandLineRunner cmdLineRunner(ApplicationContext context) {
    return args -> {
        ServletContextInitializerBeans scib = new ServletContextInitializerBeans(context,
                FilterRegistrationBean.class, DelegatingFilterProxyRegistrationBean.class);
        System.out.println("----");
        scib.iterator().forEachRemaining(s -> {
            System.out.println(s);
        });
    };
}

And it will print out something like :

characterEncodingFilter urls=[/*] order=-2147483648
formContentFilter urls=[/*] order=-9900
requestContextFilter urls=[/*] order=-105
springSecurityFilterChain urls=[/*] order=-100
deviceFilter urls=[/*] order=1
mdcFilter urls=[/*] order=2
filterRegistrationBean urls=[/*] order=2147483647
dispatcherServlet urls=[/]
Ken Chan
  • 84,777
  • 26
  • 143
  • 172
  • Very nice Ken, and thanks for the help. This works and I can confirm that my filters are after springSecurityFilterChain. – RRGT19 Jul 17 '23 at 19:20