Search code examples
springspring-bootspring-security

How to have different filters for different request paths on spring security?


I have two filters and I want to apply one on "/relatorios/**" and another for the rest.

How to do it?

Here is my (not working) version:

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.antMatcher("/relatorios/**")
                    .csrf()
                    .disable()
                    .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                    .addFilter(relatoriosFilter)
                    .addFilterBefore(new ExceptionTranslationFilter(new Http403ForbiddenEntryPoint()),
                                     relatoriosFilter.getClass())
                    .authorizeRequests()
                    .and()
                    .antMatcher("/**")
                    .csrf()
                    .disable()
                    .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                    .addFilter(authHeaderTokenFilter)
                    .addFilterBefore(new ExceptionTranslationFilter(new Http403ForbiddenEntryPoint()),
                                     authHeaderTokenFilter.getClass())
                    .authorizeRequests()
                    .anyRequest()
                    .authenticated();
    }

UPDATE:

With this version, when I call any request path, both filters are called. I want "/relatorios/**" to call one filter and everything else to call another filter.


Solution

  • Here are two ways to define the URL paths that filters are applied on.

    Firstly, you should be aware that creating a bean of a filter class implementing the Filter interface, the filter is then automatically registered to all endpoints. Since you are trying to achieve different filters for different paths, remove this if you are doing so in your code.

    Now you may register your filters in one of the two following methods.

    Method 1 - Register Filters with FilterRegistrationBean

    In this method, your defined security chain should not define your customer filters, so remove both the addFilter methods from there. You will be setting the paths not via the chain, but rather via registration beans.

    @Configuration
    public class FilterConfiguration {
    
        @Bean
        public FilterRegistrationBean<RelatoriosFilter> relatoriosFilter(){
            FilterRegistrationBean<RelatoriosFilter> registrationBean
                    = new FilterRegistrationBean<>();
    
            registrationBean.setFilter(new RelatoriosFilter());
            registrationBean.addUrlPattern("/relatorios/*");
            registrationBean.setOrder(ORDERED.HIGHEST_PRECEDENCE);
    
            return registrationBean;
        }
    
        @Bean
        public FilterRegistrationBean<AuthHeaderTokenFilter> filter2(){
            FilterRegistrationBean<AuthHeaderTokenFilter> registrationBean
                    = new FilterRegistrationBean<>();
    
            registrationBean.setFilter(new AuthHeaderTokenFilter());
            registrationBean.addUrlPattern("/*");
            registrationBean.setOrder(ORDERED.HIGHEST_PRECEDENCE);
    
    
            return registrationBean;
        }
    
    }
    

    In this manner, you should also control the order of filters by calling setOrder method and giving a lower number for a higher precedence in the filtering chain. The necessary order will depend on the version of Spring you are using and in which part of the chain you are interested to inject in the chain. In my example, it will be the first filter.


    Method 2 - Splitting the WebSecurityConfigurerAdapter configurations

    A WebSecurityConfigurerAdapter chain cannot define two different filter configurations by path matching. This is one of various limitations of this chain.

    This can be solved easily by creating an additional configuration, so that each configuration matches a different path and applies a different filter. You will end up with two configurations.

    Configuration 1

    @Configuration
    @Order(1)
    public class RelatoriosSecurity extends WebSecurityConfigurerAdapter {
    
    
        @Override
        protected void configure(HttpSecurity httpSecurity) throws Exception {
            httpSecurity
                    .authorizeRequests()
                    .and()
                    .antMatcher("/relatorios/**")
                    .addFilterBefore(new RelatoriosFilter(), ChannelProcessingFilter.class)
                    .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                    .authorizeRequests().anyRequest().authenticated();
        }
    
    }
    

    Configuration 2

    @Configuration
    @Order(2)
    public class GeneralSecurity extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity httpSecurity) throws Exception {
            httpSecurity
                    .antMatcher("/**")
                    .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                    .addFilterBefore(new AuthHeaderTokenFilter(), ChannelProcessingFilter.class)
                    .authorizeRequests().anyRequest().authenticated();
        }
    
    }
    

    In this method, @Order annotation defines the order of execution of the chains, so much like your original solution, the RelatoriosSecurity chain will be executed before GeneralSecurity chain. Also, the addFilterBefore will define before which filter the supplied filter will run. The class of the of the filter to come before will depend on your Spring version, but in mine ChannelProcessingFilter is the first, therefore our supplied filter will be executed first, before ChannelProcessingFilter.