Spring Boot Security Configuration, practically explained – Part5: From WebSecurityConfigurerAdapter to SecurityFilterChain
221013-19
Intro
This is the 5th in a series of posts of mine about Spring Security. All the previous posts have dealt with example customizations that can be done on the basis of the implementation of a custom class extending the WebSecurityConfigurerAdapter. However, since Spring Security version 5.7.0-M2, the widely used WebSecurityConfigurerAdapter is not the case anymore, it has been deprecated. So, here, in this article, we will see how we can have such security customizations without using the WebSecurityConfigurerAdapter.
Note: Since the WebSecurityConfigurerAdapter is widely used, someone can still rely on it, even without the annoying warnings (curly yellow underlines). Read the post below on how to deal with that.
From WebSecurityConfigurerAdapter to SecurityFilterChain
Going back to implementations based on the abstract class of the WebSecurityConfigurerAdapter, recall that one has to make a custom class that extends it, and thus she/he has to work by overriding its 2 important contract methods:
Those 2 methods are nothing but “configurers” that allow us to customize the Spring Boot security that suits our project needs.
Below is a very simple example of such customization for protecting a REST API on the basis of the HTTP Basic Authentication and using just the in-memory Authentication Provider.
Both of the overridden methods of the WebSecurityConfigurerAdapter, are used to form the necessary beans in the Spring security context.
Now, pay attention that only the HttpSecurity configurer has to be overridden when we use the WebSecurityConfigurerAdapter. The other one, the configurer for an AuthenticationManager (which actually is used to configure an AuthenticationManagerBuilder to build and return an AuthenticationManager), can be implemented as a bean (via a @Bean annotated method) even the fact that we use the WebSecurityConfigurerAdapter. The result is absolutely the same. So, on the basis, of the previous code example, this is how one can do that:
The HttpSecurity configurer is responsible to secure our app’s URL paths and endpoints. In our case above, we protect the “api/items” endpoint, and the way we do that is to use the HTTP Basic Authentication standard (via a pair of username and password).
Note, that providing an AuthenticationManager as a @Bean, makes it available globally, to the entire application and for any endpoint protected via http security. So, the authentication manager in the above example is a global one. However, we can make it specific to our protected endpoint, and in this case, it is considered a local authentication manager. In this case, we have just to “inform” the HttpSecurity configurer, to use the bean method. To do so we can just uncomment the commented line in the example above. We can also comment out the @Bean annotation from the authManager() method as well.
The filter chain
The manner Spring actually works with HttpSecurity, is through a number of security filters. Actually, it creates a security filter chain. Take a closer look at the output in the debug console when you run the application:
As you can see, the Spring framework takes the settings in our custom security configuration and puts them in a number of pre-defined (default) security filters. The list of those filters is actually an ordered list of filters which is managed by Spring, and all of them form a DefaultSecurityFilterChain. (You can find the default filter ordering here).
So, the “new” way of security that it is supported by Spring, is to use our custom http security filters (in the security filter chain), instead of configuring them via a class that has to extend the WebSecurityConfigurerAdapter. In essence, Spring allows us to configure our HttpSecurity using a bean method that builds and returns our custom implementation of the SecurityFilterChain interface.
Actually, using a SecurityFilterChain bean is not a new approach. It exists since version 3.1 of the Spring Security, and has been used to configure a FilterChainProxy bean. (You can go deeper here and here). Thus, one could have implemented it even before the deprecation of the WebSecurityConfigurerAdapter. However, now seems that using SecurityFilterChain bean becomes the only officially recommended and supported approach.
[Note: Since we will not use the deprecated WebSecurityConfigurerAdapter anymore, you can remove, any prior version of the “spring-boot-starter-security” dependency in our project’s pom.xml file. Moreover, we can upgrade the “spring-boot-starter-parent” to the 2.7.6 version (Not 3.0.0. yet)].
You can find a simple example of implementing such a SecurityFilterChain bean, in the official documentation here. The example consists just of 2 necessary beans, one for the SecurityFilterChain and one providing an in-memory InMemoryUserDetailsManager/UserDetailsService.
However, on the basis of the last example, we have seen above, we can create a CustomeSecurityConfiguration class similar to the one below, which is not that different from what we have done before:
Note, that besides the fact that using a separate AuthenticationManager method as a @Bean makes it available globally, it also allows us to add more Authentication Providers in the future.
Using an AuthenticationProvider with UserDetailsService and PasswordEncoder.
As you probably have noticed above, we don’t give any user details with the inMemoryAuthentication(), so, we have also to use a UserDetailsService. This avoids us from having any issues caused by the Spring Boot default settings. For instance, Spring Security by default (without any security customizations) creates a UserDetailsService bean with a username of “user” and a randomly generated password that is logged to the console. So, minimum customization should include a custom UserDetailsService.
Thus, a better approach is to have in place a UserDetailsSerivice, as well as a PasswordEncoder. Below, you can find such an example, which uses a very simple in-memory UserDetailsService.
We also use a PasswordEncoder that works with the DelegatingPasswordEncoder.
Here we use the deprecated NoOpPasswordEncoder, just for demo purposes, so you can use the annotation @SuppressWarnings(“deprecated”) to suppress the warning.
Furthermore, while one can use any AuthenticationProvider, the example below uses the widely used DaoAuthenticationProvider, which accepts and can be used with any implementation of UserdetailsService and any PasswordEncoder. That all said, find below the whole example:
Since the recent Spring Security versions promote developers to use lambdas (actually DSL lambdas), the example below shows, how the http authorized requests of the above SecurityFilterChain bean can be re-written using lambdas:
. . . @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeRequests((athReqs) -> athReqs.antMatchers("/api/items").hasRole("USER")) .httpBasic() //.and().authenticationManager(authManager(http)) ; return http.build(); } . . .
So far, so good. I hope you gained an understanding of how you can start implementing Spring Security the “new way”.
Find the final repo here.
A Local authentication manager version
Previously, we have talked about global and local authentication providers. So, in the case where someone wants to implement a local authentication manager, the code below shows an updated version of the previous “CustomSecurityConfiguration” class, where we use the authentication manager as a local one:
Bonus code: Security configuration with CustomAuthebticationProvider the “new way”
In my previous post below, we used the CustomSecurityConfiguration class extending the WebSecurityConfigurerAdapter.
So, the “bonus-code” here is the updated version of the CustomSecurityConfiguration class that follows the SecurityFilterChain and been-only approach, and which also uses functional style with just lambdas. Find it below:
Using DSL-based customizations
A last but not least reference in this post is about the DSL (Domain-Specific Language) and how we can use DSL-based configurers.
DSL – Domain-Specific Language with Java
Probably, an area that one can deal mostly with DSL and Java is Spring Integration (e.g. messaging). Nonetheless, DSL-based customizations seem also gaining some promotion by Spring Security.
Generally, you should be aware that the HTTPSecurity configurers and builders we have met so far in our security customizations are based on implementation classes that follow the DSL principles. Such DSL-based classes usually are invoked using a functional programming style making the process of configuration more user-friendly, and closer to human language. Moreover, each customization a DSL-based class/configurer allows us to perform, usually results in an updated instance of that configurer, letting further customizations.
Going further into DSL concepts is beyond the goals of this post. Anyone can search the web and find dozens of related posts. Just, for your convenience, I have selected some nice articles providing practical examples to start with the subject. These are the links:
- https://medium.zenika.com/builder-pattern-a-first-step-to-dsl-1f5645d3ec84
- https://medium.com/the-kotlin-primer/domain-specific-languages-867f2790a700
- https://www.infoworld.com/article/2077891/scripting-jvm-languages-creating-dsls-in-java-part-3-internal-and-external-dsls.html
- https://www.javacodegeeks.com/2013/06/creating-internal-dsls-in-java-java-8-adopting-martin-fowlers-approach.html
On the same subject, I’d like also to mention the quite interesting book “Modern Java in Action Lambdas, streams, functional and reactive programming 2nd Ed – 2018, Manning publications” and especially Chapter 10: “Domain-specific languages using lambdas”.
However, I think it a good idea to provide you with some example code below.
What we are going to do
Basically, following the official examples here or here, we are going to apply our custom configurer (= our custom DSL class) to the HttpSecurity of the SecurityFilterChain. As a basis, let’s use the updated repo of my aforementioned post, based on the previous bonus-code. Find the repo here.
Before proceeding with a custom DSL class, let’s first start by making a custom header filter.
A Custom Header Filter
As we have already said, the way Spring actually works with HttpSecurity, is through a number of security filters. This means that we can also add our own filter. As an example, we will use a custom filter that ads a custom header to the returned response of a request, allowing us to provide some more info via the returned headers.
Running a project like the one of our starting repo, you can see in the terminal or debugging console all the filters applied in the DefaultSecurityFilterChain, and probably you can notice the BasicAuthenticationFilter.
o.s.s.web.DefaultSecurityFilterChain : Will secure any request with [ org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@501760bd, org.springframework.security.web.context.SecurityContextPersistenceFilter@55a1effd, org.springframework.security.web.header.HeaderWriterFilter@468057f5, org.springframework.security.web.csrf.CsrfFilter@2b1cf627, org.springframework.security.web.authentication.logout.LogoutFilter@330be2b0, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@2af908d9, <---org.springframework.security.web.savedrequest.RequestCacheAwareFilter@3050b83d, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@74ab6e3d, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@5c1a251d, org.springframework.security.web.session.SessionManagementFilter@19c08f2c, org.springframework.security.web.access.ExceptionTranslationFilter@2819b1ad, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@5504d510]
Moreover, using the Postman you can take a look at the headers returned with a response. In our case below, you can see that the number of headers is 11:
Basically, one can make a custom filter either by implementing the basic Filter interface or by extending one of the “ready-made” implementation classes offered by Spring. In our example we are going to extend the GenericFilterBean class (which of course implements the Filter interface).
The main job is done by overriding the doFilter() method, in which we gain access to request and response servlet objects, as well as to the FilterChain object. So, this method is the right place to set up our custom header. Let’s name the class of our custom filter “CustomResponseHeaderFilter”. The example code is given below:
The next step is to add/apply it in the SecurityFilterChain bean of our “CustomSecurityConfiguration” class. We can add it at the same ordering position as the BasicAuthenticationFilter. Thus, the code of the bean becomes:
After running the project, you can check the output in the terminal or debugging console:
o.s.s.web.DefaultSecurityFilterChain : Will secure any request with [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@38524eae, org.springframework.security.web.context.SecurityContextPersistenceFilter@53eb7394, org.springframework.security.web.header.HeaderWriterFilter@31adeeb1, org.springframework.security.web.csrf.CsrfFilter@13708c79, org.springframework.security.web.authentication.logout.LogoutFilter@12941fd0, com.zzpzaf.restapidemo.Configuration.CustomResponseHeaderFilter@62729f4c, <--------org.springframework.security.web.authentication.www.BasicAuthenticationFilter@7759829b, <---org.springframework.security.web.savedrequest.RequestCacheAwareFilter@795e0fe5, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@2758be19, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@4d6b8bf4, org.springframework.security.web.session.SessionManagementFilter@446f2c24, org.springframework.security.web.access.ExceptionTranslationFilter@c94d175, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@cc46e58]
Again, using Postman, you can also check that our custom header now has been added to the returned headers (now the number is 12):
OK. We did it. Now let’s proceed further and see how we can do that using a custom DSL class.
We can name our custom DSL class “CuctomDslHttpSecurityConfiguration”. This will be our own custom configurer that should be applied at HttpSecurity configuration in the SecurityFilterChain bean, so it should extend the AbstractHttpConfigurer.
Following the official example here, our custom DSL example code can be similar to the one below:
The main job is done inside the configure() method where we set up and add as filter, our CustomResponseHeaderFilter class. Also, notice that we use a setter method that will allow us to pass down to the CustomResponseHeader and set up the header name and the header value. Moreover, as you can see we used another httpSecurity configuration making the HTTP session stateless in the init() method. Thus, there would not be necessary to do it in the configuration of the SecurityFilterChain bean (as we have already done before).
Now, we are ready to apply our CuctomDslHttpSecurityConfigurer class to the SecurityFilterChain bean. We do that, by simply “applying” it:
That’s it for now!
You can find the final repo here.
I hope I managed to give you some grasp on migrating from to WebSecurityConfigurerAdapter to SecurityFilterChain, and also a taste of how you can work with a custom filter and a custom DSL-based configurer.
Thanks for reading ? ! and stay tuned!