In my project (Spring Boot + Security + Thymeleaf) I wanted to configure custom web security as is described in "getting started" article on the Spring's official web. I've followed steps in the article and created my custom configuration. But I forgot to add a @EnableWebSecurity
annotation. Everything seemed to work fine. Except for Thymeleaf's sec:authorize-url
attribute.
What happened?
By not specifying @EnableWebSecurity
annotation I didn't disable Spring Boot's default security auto-configuration in SpringBootWebSecurityConfiguration.ApplicationWebSecurityConfigurerAdapter
. This creates a security configuration (HttpSecurity
object) that disables all access to application for unauthorised users, except for some static resources, and creates a user with a role ROLE_USER
and generates some random password for testing purposes.
My configuration also created HttpSecurity
object with my own security configuration and my own user repository.
Both HttpSecurity
configurations has been passed to WebSecurity
which is responsible for creating Spring Security Filter Chain. As you may know a security request goes through this chain until one of the filters "catches" it and process it.
Because my custom configuration bean had a higher priority, it was located "on a higher place" in security filter chain. So all requests has been caught and processed by my filter and not by the filter created by Spring Boot auto-configuration. So far so good.
Problem with two HttpSecurity configurations
When configured, the WebSecurity
object holds an instance of FilterSecurityInterceptor
. There is only one field that can hold the interceptor so no more than one interceptor can be held by WebSecurity
object.
The FilterSecurityInterceptor
is the crucial part of the Spring Security project. Actually it is its parent class AbstractSecurityInterceptor
and its implementations that makes Spring Security breath.
-
FilterSecurityInterceptor
interceptsServletRequest
s and decides whether a current user has permission to proceed or not. TheFilterSecurityInterceptor
uses a help of other classes likeSecurityContextHolder
(holds information about current user, his security context),AccessDecisionManager
(evaluates a request against a current security context) and so on. -
MethodSecurityInterceptor
intercepts method calls similarly toFilterSecurityInterceptor
. Uses Spring's proxies. -
AspectJMethodSecurityInterceptor
similar toMethodSecurityInterceptor
with support ofAspectJ
.
The FilterSecurityInterceptor
is based on information in HttpSecurity
. In WebSecurityConfigurerAdapter.init
method, the Spring populates WebSecurity
by the FilterSecurityInterceptor
instance. If more than one FilterSecurityInterceptor
are created later overwrites interceptor that is already present in WebSecurity
!
Thymeleaf-Spring Security integration gets the FilterSecurityInterceptor
from WebSecurity
and uses it to evaluate value of sec:authorize-url
attribute. Obviously, in this case it gets Spring Boot's default configuration which is not what I wanted.
So, don't forget to disable default configuration by adding @EnableWebSecurity
annotation to your security configuration class.
Update 2020: Changes in Spring Boot 2.x
Even though ths article aims at Spring Boot 1.5, in the latest versions of Spring Security there has been some changes worth mentioning.
-
The custom
@EnableWebSecurity
annotation won't disable the security auto-configurations.In Spring Boot 2.x, the custom implementations of the
WebSecurityConfigurationAdapter
abstract class or theSecurityFilterChain
interface will disable default web security auto-configuration.Spring Boot 2.4.0 also introduced the
@ConditionalOnDefaultWebSecurity
which is applied to bunch of security auto-configurations, simplifying previous auto-configuration conditionals. This annotation makes sure security auto-configurations are disabled. You can check its usage to see which configurations are being affected. -
The
@EnableWebSecurity
annotation will be added automatically if it is missing in your configuration.The annotation imports a few configurers, which makes sure your security is properly configured.
Spring Boot introduced
WebSecurityEnablerConfiguration
which will add the annotation if it is present on a classpath, and it hasn't been configured yet.
Changes in Spring Boot 2.x makes sure that problem described in this article won't happen anymore. Due to that the security configuration is a bit more straightforward and easier to do.