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.
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=[/]