
In a recent blog post, the Spring Security team announced that they’ll be deprecating the configuration setup where you subclass the WebSecurityConfigurerAdapter. Instead, you simply define beans that allow you to set up the SecurityFilterChain by calling methods on the HttpSecurity object, and configure the WebSecurity object using a configurer.
These options have been available for a while, but if you had a working configuration then there wasn’t really a compelling need to rewrite your configuration. With the announcement that your configuration will eventually stop working, there’s an actual incentive.
Therefore I tried to update the configuration for my current project, culminating in this blog.
A Love Triangle
Our configuration defines a custom AuthenticationProvider, which knows how to accept a custom JWT Bearer token and then checks that against one of two configured AWS Cognito identity providers, as we have a dual region setup running on AWS (I might once write a separate blog post on our Cognito setup and Spring Security; ping me if you’re interested).
This is accompanied by a custom authentication filter, which creates a custom Authentication token holding the JWT and passes that to the provider via the AuthenticationManager.
This is quite a common setup if you want to extend the framework with your own custom authentication mechanism: the idea is that the filter only knows how to create the Authentication token, and it’s the AuthenticationProvider that uses that token to come up with an Authentication object that represents an authenticated principal by using the provided info to perform the actual authentication.
In short, that means that:
- The provider needs to be registered with the AuthenticationManager
- Our filter needs to be injected with the AuthenticationManager
- The filter needs to be inserted into the SecurityFilterChain
- Only after that chain has been created, the AuthenticationManager is created
Ouch… We’re running into an unhealthy chain of dependencies here for our filter. So how do we untangle this unfortunate triangle?
Some Method That I Used To Know
In the situation where you extend the WebSecurityConfigurerAdapter, this problem is solved by providing you with an authenticationManagerBean method. It returns what’s effectively a lazy-loading proxy to the real AuthenticationManager, so you can inject that into your filter before the real manager has been created.
The typical way to use it is to override it so that you can annotate it with @Bean:
@Bean(name = "authenticationManager") @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); }
Now the AuthenticationManager is simply available as a bean that can be dependency injected.
However, with the component-based setup there are no helper methods like that anymore. It wasn’t obvious to me how to address this, so I set out to try some options.
Relationship Experiments
One of the first things I tried, after looking at how Spring Security itself deals with some of these issues, was to inject the HttpSecurity into the @Bean method that created the filter so that I could call httpSecurity.getSharedObject(AuthenticationManager.class) to obtain the AuthenticationManager, allowing me to pass it to the filter.
However, when that bean method is called that manager isn’t created yet, so that code simply returns null.
I experimented with some other setups as well, many of them actually resulting in a circular dependency from the configuration class on itself, preventing our Spring Boot services from starting up (this is not allowed by default since Spring Boot 2.6).
Happily Ever After?
After some further investigation I understood that Spring Security is still providing an AuthenticationManagerBuilder bean that you can use for configuration, which eventually creates the AuthenticationManager that we’re looking for.
For configuration purposes, you can autowire it in your config class, but you have to ensure that your class is annotated with @EnableGlobalAuthentication: either directly, or by using another annotation that has this as a meta-annotation. For example, this is how we can register our custom provider:
@Autowired void registerProvider(AuthenticationManagerBuilder builder) { builder.authenticationProvider(new CognitoJwtAuthProvider()); }
I figured the builder would let me access the manager that it eventually builds, but of course by the time that all this configuration is still taking place that hasn’t happened yet. Spring does make it easy for components to receive a callback when the context is completely created / refreshed using the SmartInitializingSingleton interface, so eventually this is what I ended up with.
In our configuration class:
@Bean JwtAuthenticationFilter jwtAuthFilter(AuthenticationManagerBuilder builder) { return new JwtAuthenticationFilter(builder); }
In the filter class:
public class JwtAuthenticationFilter extends OncePerRequestFilter implements SmartInitializingSingleton { private AuthenticationManagerBuilder authMgrBuilder; private AuthenticationManager authenticationManager; public JwtAuthenticationFilter(AuthenticationManagerBuilder authMgrBuilder) { this.authMgrBuilder = authMgrBuilder; } @Override public void afterSingletonsInstantiated() { this.authenticationManager = authMgrBuilder.getObject(); } … }
This does the trick, but it feels pretty awkward to set up your filter this way. I had a look at how Spring Security typically does this for its own filters: that involves additional SecurityConfigurer implementations that can set the AuthenticationManager on the filter once it becomes available, thus managing the instantiation of the filter in different steps. I don’t need / want to write such a configurer just for this purpose, so for now I guess I’ll stick to this.
If you’re in a similar situation with a custom filter that needs the AuthenticationManager, I would like to hear about better approaches.
Please leave a comment if you came up with something; after all, I’m not married to my current code 😉

Hee Joris! 🙂
Similar setup here, some custom code to deal with bearer tokens originating from Salesforce which are opaque (they’re really just session IDs) so part of our Spring Security setup is an AuthenticationProvider that does a call to Salesforce to check that the session ID is valid and exchange it for some meaningful info like the user id of the user and their role(s) that we can use to construct an Authentication object.
Looks like your blog post will save us a lot of time when we do the (it seems now) inevitable rewrite our WebSecurityConfigurerAdapter-extending Spring Security Config class with one that doesn’t extend WebSecurityConfigurerAdapter. Thanks!
Hi Joris,
I imagine this code could be rewritten using a custom DSL as follows:
public class MyCustomDsl extends AbstractHttpConfigurer {
@Override
public void configure(HttpSecurity http) throws Exception {
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
http.addFilter(new JwtAuthenticationFilter(authenticationManager));
http.authenticationProvider(new CognitoJwtAuthProvider());
}
public static MyCustomDsl customDsl() {
return new MyCustomDsl();
}
}
public class JwtAuthenticationFilter extends OncePerRequestFilter
implements SmartInitializingSingleton
{
private AuthenticationManager authenticationManager;
public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
…
}
Then, the custom DSL could be applied when configuring the SecurityFilterChain:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// …
http.apply(customDsl());
return http.build();
}
Have you tried this style of configuration?
Hey, I missed this comment when it was posted. The whole custom DSL thing is what I’m trying to avoid: it seems like a very heavyweight approach to something as simple as “obtaining the AuthenticationManager once it’s created so that it can be injected into classes that depend on it”.
I see some other suggestions have been added to the comment section, so apparently there are solutions that are a bit nicer than what I describe in my blog.
Thank Joris for the post, it really pointed me the right direction and saved me hours.
I read your following opinion:
> I don’t need / want to write such a configurer just for this purpose, so for now I guess I’ll stick to this.
I just want to mention for you and for other readers that the “SecurityConfigurer” solution is much more simple or even elegant.
It has only 2 rows of implementation:
class JwtConfigurer extends AbstractHttpConfigurer {
@Override
public void configure(HttpSecurity builder) {
AuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class);
builder.addFilterBefore(new JwtAuthenticationFilter(authenticationManager), AuthorizationFilter.class);
}
}
then you create a @Bean method and use it in configuration like http.apply(jwtConfigurer())
much less hacky and certainly not a workaround.
cheers
Thanks for the pointer! For some reason I don’t receive notifications about comments, so I missed this. This does look nice indeed, I’ll give it a go.
The described solution works well for OncePerRequestFilter. However, if I wanted to use an AbstractAuthenticationProcessingFilter based filter I needed to adapt the solution as follows:
1. add the following snippet to your AbstractAuthenticationProcessingFilter class:
@Override
@Autowired
public void setAuthenticationManager(org.springframework.security.authentication.AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
}
2. add the following bean definition to your WebSecurityConfig
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
3. bring the definitions in your WebSecurity chain together:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http, YourAuthenticationFilter yourAuthenticationFilter) throws Exception {
http
.authenticationProvider(new YourAuthenticationProvider())
.addFilterBefore(yourAuthenticationFilter, AnonymousAuthenticationFilter.class)
.authorizeRequests()
.requestMatchers(getRequestMatchers()).authenticated()
;
return http.build();
}