Spring Security and Multiple Filter Chains
21 Aug 2017Spring 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.
@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.
@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.
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
.