0

I have a WebSecurityConfigurerAdapter implementation class which already has two spring security filters

@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
    httpSecurity.csrf().disable().authorizeRequests().anyRequest().authenticated().and().
            exceptionHandling().and().sessionManagement().maximumSessions(1);

    httpSecurity.addFilterAfter(customAuthenticationFilter, BasicAuthenticationFilter.class);
    httpSecurity.addFilterAfter(customAuthorizationFilter, CustomAuthenticationFilter.class);
}

Now I want to add a new custom filter called customEntryFilter before customAuthorizationFilter i.e in between existing Spring filter chain without modifying the existing WebSecurityConfigurerAdapter implementation class. How to do this in Spring security?

Expected Filter order

customAuthenticationFilter
customEntryFilter
customAuthorizationFilter

I tried with creating a customEntryFilter by extending the OncePerRequestFilter but the filter was executed at last in the filter chain

@Component
public class CustomEntryFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        System.out.println("Going via " + CustomEntryFilter.class.getName());
        filterChain.doFilter(request, response);
    }
}

How to make this filter execute at the beginning without changing the WebSecurityConfigurerAdapterConfiguration class? Kindly help.

sujin
  • 2,813
  • 2
  • 21
  • 33
  • Does this answer your question? [Filter order in spring-boot](https://stackoverflow.com/questions/25957879/filter-order-in-spring-boot) – kasptom Aug 26 '20 at 10:58
  • @kasptom **@Order** helps to add the filter before or after the Whole Spring security chain. But my uses is I want to add the filter in between existing Spring security chain without modifying the old **WebSecurityConfigurerAdapterConfiguration** implementation clss. – sujin Aug 26 '20 at 11:03

1 Answers1

0

Short answer to your question:

@Configuration
@ComponentScan
@ConfigurationPropertiesScan
class CommonConfig {
    @Autowired
    lateinit var chains: List<SecurityFilterChain>

    @Autowired
    lateinit var myCustomFilter: MyCustomFilter

    @PostConstruct
    fun post() {
        chains.forEach {
            for (i in it.filters.indices){
                if (it.filters[i] is LogoutFilter) {
                    it.filters.add(i + 1, myCustomFilter)
                }
            }
        }
    }
}

More details below:

Let's say we have the following chain:

@Bean
@Throws(Exception::class)
fun filterChain(http: HttpSecurity): SecurityFilterChain {
    http.authorizeRequests()
        .antMatchers(HttpMethod.POST, "/first", "/second")
        .authenticated()
        .anyRequest()
        .permitAll()
        .and()
        .httpBasic()
        .authenticationEntryPoint(auditAuthenticationEntryPoint)
        .and()
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and().formLogin().disable()
        .csrf().disable()
        .cors().configurationSource(corsConfigurationSource())
    return http.build()
}

And we want to add the following filter to this chain between some specific filters:

@Component
class MyCustomFilter : OncePerRequestFilter() {
    override fun doFilterInternal(
        request: HttpServletRequest,
        response: HttpServletResponse,
        filterChain: FilterChain
    ) {
        //some work: adding headers, working with authentication or something else
        filterChain.doFilter(request, response)
    }
}

Here are a few ways to add our filter to the chain:

1) adding the @Order annotation to the filter:

@Component
@Order(5)
class MyCustomFilter : OncePerRequestFilter()

Note that:

  • If you do not specify an order in the annotation, then the default order is: Ordered.LOWEST_PRECEDENCE - your filter will be the last one in the chain.
  • If you are working with Authentication in your filter (through authentication providers) then Ordered.LOWEST_PRECEDENCE and Ordered.HIGHEST_PRECEDENCE not suitable for your case because:
    • in case Ordered.HIGHEST_PRECEDENCE filterSecurityContextPersistenceFilter which will be executed after your filter will clear the SecurityContext even if your filter added an Authentication object to the SecurityContext
    • in the case of Ordered.LOWEST_PRECEDENCE your filter will not be called at all: AnonymousAuthenticationFilter will set the Authentication object to Anonymous and you will receive a 401 or 403 response before the chain calls your filter
    • In this case, you need to get the chain and determine the specific order of your filter via the FilterChainProxy and the doFilter(ServletRequest request, ServletResponse response) method, getting this.additionalFilters. For example, 5. But this is not the best way to determine the order

2) The standard (and best) way is through setting up the filter chain itself:

    @Bean
    @Throws(Exception::class)
    fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http.authorizeRequests()
            ...
            .addFilterBefore(MyCustomFulter(), BasicAuthenticationFilter::class.java)
            ...
        return http.build()
    }

3) Injecting a filter into a chain in @PostConstruct

In my case, .addFilterBefore() was not possible due to the fact that the filter was added from the starter. My starter needs to add a filter to each chain after the LogoutFilter. Since there can be multiple chains in a context, I add a filter to each one. Thus, when adding my starter to the application , the filter is added to the required place without any additional configuration:

@Configuration
@ComponentScan
@ConfigurationPropertiesScan
class CommonConfig {
    @Autowired
    lateinit var chains: List<SecurityFilterChain>

    @Autowired
    lateinit var myCustomFilter: MyCustomFilter

    @PostConstruct
    fun post() {
        chains.forEach {
            for (i in it.filters.indices){
                if (it.filters[i] is LogoutFilter) {
                    it.filters.add(i + 1, myCustomFilter)
                }
            }
        }
    }
}
gearbase
  • 21
  • 3