Enterprise Java

Spring Security and Multiple Filter Chains

Spring Security is an immensely useful technology. It allows you to secure your application without being too intrusive and allows to plug with many different authentication mechanisms. On the other hand it is not that easy to get into and one of those tools that I have to relearn each time I am touching it. In this post I’ll describe some of the basics of spring security and how you can use it to secure different parts of your application in different ways.

Spring Security Configuration

Let’s look at a piece of configuration for Spring Security, you can find the full source code on Github. I am using Spring Boot but most parts should be the same for all Spring applications.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .httpBasic()
                .and()
                .authorizeRequests().antMatchers("/secret/**").authenticated()
                .and()
                .authorizeRequests().antMatchers("/**").permitAll();
    }
}

In the simplest case you just configure the HttpSecurity using the method chaining that is common in Spring Security. In this case we enable HTTP Basic Auth and require authentication for one endpoint (everything below /secure/). All other requests (denoted by /**) will be permitted. The patterns that are used here are of the Ant path syntax but you can also use different RequestMatcher to decide which parts of your application require which authentication.

All the functionality of Spring boot is implemented in a filter chain. The call to httpBasic() above actually just makes sure that the relevant filter is added to the filter chain. In this case the BasicAuthenticationFilter will check if there is an Authorization header and evaluate it. If one is found it will add an Authentication object to the context and execute the rest of the filter chain. At the end of the chain is the FilterSecurityInterceptor that checks if the requested resource requires authentication and if the one that is set conforms to the requested roles.

You can also exclude some parts of the application from authentication by configuring the WebSecurity. The following method makes sure that any requests to /resources/ skip the configuration above.

1
2
3
4
@Override
public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers("/resources/**");
}

Under the hood this will add an additional filter chain that is triggered for the path configured and does nothing.

Multiple Filter Chains

Sometimes it can be necessary to use different authentication mechanisms for different parts of your application. To achieve that, Spring Security allows you to add several configuration objects. It is a common practice to use inner configuration classes for this that can also share some parts of the enclosing application. The following class adds two different Spring Security filter chains.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class SecurityConfig {
    @Configuration
    public static class ApiConfiguration extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            // doesn't really make sense to protect a REST API using form login but it is just for illustration
            http
                    .formLogin()
                    .and()
                    .authorizeRequests().antMatchers("/secret/**").authenticated()
                    .and()
                    .authorizeRequests().antMatchers("/**").permitAll();
        }
        @Override
        public void configure(WebSecurity web) throws Exception {
            web.ignoring().antMatchers("/resources/**");
        }
    }
    @Order(1)
    @Configuration
    public static class ActuatorConfiguration extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .antMatcher("/management/**")
                    .httpBasic()
                    .and()
                    .authorizeRequests().antMatchers("/management/**").authenticated();
        }
        @Override
        public void configure(WebSecurity web) throws Exception {
            super.configure(web);
        }
    }
}

Both of the classes inherit from the adapter configuration class and configure their HttpSecurity. Each of those classes adds a filter chain and the first one that matches is executed. The @Order annotation can be used to influence the order of the filter chains to make sure that the right one is executed first.

It can also be necessary to restrict the filter chain to only a certain part of the application so that it is not triggered for other parts. The ActuatorConfiguration is restricted to only match requests to /management/. Be aware that there are two different places in the configuration that accept a RequestMatcher. The one at the beginning restricts the url the filter chain is triggered for. The ones after authorizeRequests() are used to define which requests require what kind of authentication.

Note that configuring the WebSecurity is not tied to one of the HttpSecurity configurations as those add their own filter chain, only the order might be different. If you add a pattern in both configurations it will even operate on the same instance of WebSecurity.

One last thing: In case you are using a custom authentication filter (e.g. for token based authentication) you might have to take care that you don’t register your filter as a Servlet Filter as well. You can influence that by configuring a method returning a FilterRegistrationBean and accepting an instance of your Filter. just create a new FilterRegistrationBean for your filter and set enabled to false.

Reference: Spring Security and Multiple Filter Chains from our JCG partner Florian Hopf at the Dev Time blog.

Florian Hopf

Florian is a software developer living in Singapore. He enjoys building search solutions based on Lucene, Solr and Elasticsearch and he's interested in topics like build automation and test driven development.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button