79

How can I specify order of my Filter in spring-boot? I need to insert my MDC filter after Spring Security filter. I tried almost everything but my filter was always first. This didn't work:

@Bean
@Order(Ordered.LOWEST_PRECEDENCE)
public UserInsertingMdcFilter userInsertingMdcFilter() {
    return new UserInsertingMdcFilter();
}

This didn't work too:

@Bean
public FilterRegistrationBean userInsertingMdcFilterRegistrationBean() {
    FilterRegistrationBean registrationBean = new FilterRegistrationBean();
    UserInsertingMdcFilter userFilter = new UserInsertingMdcFilter();
    registrationBean.setFilter(userFilter);
    registrationBean.setOrder(Integer.MAX_VALUE);
    return registrationBean;
}
igo
  • 6,359
  • 6
  • 42
  • 51
  • 7
    `@Order(Ordered.LOWEST_PRECEDENCE + 100)` will not work because `Ordered.LOWEST_PRECEDENCE = Integer.Max` and Integer.Max + 100 = some negative number, this would mean a very high precedence – Ralph Sep 21 '14 at 09:57
  • @Ralph - I also tried event `@Order(Ordered.LOWEST_PRECEDENCE)` with no success. – igo Sep 21 '14 at 10:15
  • What version of Spring Boot? Did you try 1.1.7? – Dave Syer Oct 01 '14 at 10:28
  • @DaveSyer, I tried, no difference – igo Oct 01 '14 at 18:04
  • 2
    Possible duplicate of [How to define Servlet filter order of execution in Spring Boot application](http://stackoverflow.com/questions/22453707/how-to-define-servlet-filter-order-of-execution-in-spring-boot-application) – OrangeDog Apr 15 '16 at 16:49

6 Answers6

62

Guys from Spring helped again. See the following:

From the former:

Spring Security doesn't set an order on the Filter bean that it creates. This means that, when Boot is creating a FilterRegistrationBean for it, it gets the default order which is LOWEST_PRECEDENCE.

If you want your own Filter to go after Spring Security's you can create your own registration for Spring Security's filter and specify the order.

So the answer to my question is:

@Bean
public FilterRegistrationBean securityFilterChain(@Qualifier(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME) Filter securityFilter) {
    FilterRegistrationBean registration = new FilterRegistrationBean(securityFilter);
    registration.setOrder(Integer.MAX_VALUE - 1);
    registration.setName(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);
    return registration;
}

@Bean
public FilterRegistrationBean userInsertingMdcFilterRegistrationBean() {
    FilterRegistrationBean registrationBean = new FilterRegistrationBean();
    UserInsertingMdcFilter userFilter = new UserInsertingMdcFilter();
    registrationBean.setFilter(userFilter);
    registrationBean.setOrder(Integer.MAX_VALUE);
    return registrationBean;
}
Manuel Jordan
  • 15,253
  • 21
  • 95
  • 158
igo
  • 6,359
  • 6
  • 42
  • 51
  • See also discussion in https://github.com/spring-projects/spring-boot/issues/677 and https://github.com/spring-projects/spring-boot/issues/1640 – Hendy Irawan Jun 20 '15 at 07:41
  • 5
    Given `HIGHEST_PRECEDENCE = -2147483648` and `LOWEST_PRECEDENCE = 2147483647` I would assume lowest precedence would go last? Everything would _go before it_? – ianstarz May 19 '18 at 16:58
  • 4
    It should go last. From JavaDoc of @Order: *Lower values have higher priority. The default value is Ordered.LOWEST_PRECEDENCE, indicating lowest priority (losing to any other specified order value).* – lrxw Jun 06 '18 at 06:56
  • 4
    @OrangeDog Precedence != priority – Nic Nov 04 '18 at 02:30
  • @Irxw plus one for the lowest value having highest priority! – Gaurav Mar 10 '20 at 07:35
31

Here's an answer compatible with Spring Boot 2 / Spring Security 5 that will allow you to insert your filter in an arbitrary place in the filter chain.

My use case was a custom logging javax.servlet.Filter that I wanted to execute before any Spring Security filters; however the below steps should allow you to put a filter anywhere in your existing Spring filter chain:

Step 1: Find out the order of Spring filters in your existing setup.

Connect your favorite remote debugger to your application, and set a breakpoint in the doFilter(ServletRequest request, ServletResponse response) method of org.springframework.security.web.FilterChainProxy.

As of Spring Security 5.1.6, that is line 311. In your debugger, find out the existing filters by inspecting this.additionalFilters. In my application, the order was something like:

0: WebAsyncManagerIntegrationFilter
1: SecurityContextPersistenceFilter
2: HeaderWriterFilter
...

Step 2: Insert your filter in the desired place using Spring's WebSecurityConfigurerAdapter and HttpSecurity

You likely already have a WebSecurityConfigurerAdapter with a @Override configure(HttpSecurity http) method. HttpSecurity exposes addFilterBefore and addFilterAfter methods to allow you to place your filter relative to an existing class in the chain. Your filter (instance) is the first argument of these methods, and the class of the filter you'd like to insert before or after is the second argument.

In my case, I wanted my custom logging filter to be first in the chain (my code snippet is Kotlin, I'll leave the Java implementation to you):

override fun configure(http: HttpSecurity) {
    http
        .addFilterBefore(MyCustomLoggingFilter(), WebAsyncManagerIntegrationFilter::class.java)
        .authorizeRequests()
        .antMatchers(
        ...
        )
}

Step 3: Profit!

Use the debugging method described in Step 1 above to verify that your filter is where you intended in the filter chain.

Hope this helps someone else out there.

Manuel Jordan
  • 15,253
  • 21
  • 95
  • 158
Nate Vaughan
  • 3,471
  • 4
  • 29
  • 47
25

This was fixed in Spring Boot 1.2. The security chain now defaults to order 0.

It can also be set via properties:

security.filter-order=0 # Security filter chain order.

https://github.com/spring-projects/spring-boot/issues/1640

OrangeDog
  • 36,653
  • 12
  • 122
  • 207
Mikhail
  • 2,690
  • 4
  • 28
  • 43
4

Note that the Spring Security filter chain is not all there is when it comes to filters. In fact, there is a complete (non-security) filter chain at work, with one of the filters being an instance of DelegatingFilterProxy which (nomen est omen) delegates to another filter called FilterChainProxy which manages its own sublist of filters, all geared towards security related topics. This pattern has the advantage that all security filters are in one place and ordered correctly with respect to each other. This however doesn't help you at all if you need a filter to be executed either before or after all security filters. While it is true that you can configure this using the HttpSecurity object by selecting either the first or last filter in that list, it is logically weird, since chances are your filter has nothing at all to do with security. If you set a breakpoint in DelegatingFilterProxy::doFilter(ServletRequest, ServletResponse, FilterChain) you will see the entire filter chain of the application. You can even drill into the FilterChainProxy to find all the spring security filters if you want to. Then you can open the relevant classes one by one to find out their configured orders. Once you know, where your filter ought to be, you can annotate it.

If for example you need to configure a logging filter and you want to log all security failures (needs to go before FilterChainProxy) but also want a useful tracing ID (needs to go after LazyTracingFilter which has a configured default order of TraceHttpAutoConfiguration.TRACING_FILTER_ORDER = Ordered.HIGHEST_PRECEDENCE + 5) you can annotate your filter with @Order(Ordered.HIGHEST_PRECEDENCE + 6).

Yasammez
  • 1,383
  • 12
  • 15
2

add

logging.level.web=debug

in your application.properties, you can see details of the registered filters, including their order and URL patterns when springboot onstartup.

Mike Liu
  • 41
  • 3
  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/late-answers/30703700) – Hanchen Jiang Jan 01 '22 at 22:51
1

in your first case ,it is a error config,and spring's docs has special reminder :

You cannot configure the order of a Filter by annotating its bean method with @Order.

and you can find from this: https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-embedded-container-servlets-filters-listeners-beans

hexsmith
  • 576
  • 5
  • 3