Context
The purpose of my module/starter is to be used by other applications in the company I work in order to publish automatically specific logs related to authentication and authorization via ApplicationListeners using Spring Security's and OAuth2's strategy for publishing authentication events (DefaultAuthenticationEventPublisher).
Expected behaviour: using old WebSecurityConfigurerAdapter My old security config code based on Spring Boot 2.6 worked perfectly fine:
@Configuration @EnableWebSecurity
public class ResourceServerConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
. . .
.and()
.oauth2ResourceServer()
.jwt();
}
}
Description of the bug
After upgrading to Spring Boot 2.7 and replacing the usage of deprecated WebSecurityConfigurerAdapter class in favour of a config method @Bean that returns SecurityFilterChain as recommended in this article and release notes my applications don't publish automatically anymore because they do not have a valid AuthenticationEventPublisher as before.
How to reproduce it
@Configuration @EnableWebSecurity
public class ResourceServerConfig {
@Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
. . .
.and()
.oauth2ResourceServer()
.jwt();
return http.build();
}
}
Cause of the bug
The reason for this problem is: the object BearerTokenAuthenticationFilter uses an instance of ProviderManager as AuthenticationManager (so same way when using WebSecurityConfigurerAdapter). But as default an instance of ProviderManager declares its AuthenticationEventPublisher this way:
from: https://github.com/spring-projects/spring-security/blob/main/core/src/main/java/org/springframework/security/authentication/ProviderManager.java
public class ProviderManager implements AuthenticationManager, . . . {
. . .
private AuthenticationEventPublisher eventPublisher = new NullEventPublisher();
More details about the bug
So this is the problem: NullEventPublisher is a useless implementation which doesn't publish events.
Why this problem just now? When using WebSecurityConfigurerAdapter - this class was setting an instance of DefaultAuthenticationEventPublisher to ProviderManager's eventPublisher attribute over its default value NullEventPublisher's instance.
Note: I also tried using Spring Security autoconfiguration which is org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration#authenticationEventPublisher() from spring-boot-autoconfigure - but this one is never injected into ProviderManager.
Expected behaviour via a workaround - but I'm not fine with it After some tests I was able to "fix the problem" with the following code:
@Configuration
@ConditionalOnClass({AuthenticationEventPublisher.class, JwtAuthenticationProvider.class})
public class SpringConfiguration { //global configuration for several other applications using my module/starter
@Bean
public AuthenticationEventPublisher eventPublisher(ApplicationEventPublisher application) {
DefaultAuthenticationEventPublisher authentication =
new DefaultAuthenticationEventPublisher(application);
authentication.setDefaultAuthenticationFailureEvent(AuthenticationFailureBadCredentialsEvent.class);
return authentication;
}
@Bean
public ProviderManager providerManagerAvecDefaultAuthenticationPublisher(@Lazy JwtDecoder jwtDecoder, AuthenticationEventPublisher authenticationPublisher) {
JwtAuthenticationProvider authenticationProvider = new JwtAuthenticationProvider(jwtDecoder);
ProviderManager providerManager = new ProviderManager(Arrays.asList(authenticationProvider));
providerManager.setAuthenticationEventPublisher(authenticationPublisher);
return providerManager;
}
}
And also had to adjust my security configuration:
@Configuration @EnableWebSecurity
public class ResourceServerConfig {
@Autowired ProviderManager manager; //1
@Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
. . .
.and()
.oauth2ResourceServer()
.jwt()
.authenticationManager(manager); //2
return http.build();
}
}
But I have some big concerns:
-
As my module/application is to be used by other applications - this "workaround" solution will force dozens of applications to add the lines followed by comments 1 and 2
-
By adding a custom
ProviderManagerI am not aware of the risks of "forcing" a pre-built one for those applications So finally my question here is: Is there a way to bypasseventPublisher = new NullEventPublisher()fromProviderManagerwithout forcing to configure oauth2ResourceServer().authenticationManager(manager) in all applications configuring itsSecurityFilterChain? -
I had to use
@Lazyfor the injection ofJwtDecoder- and also I'm not aware how this will affect different other applications
Comment From: marcusdacoregio
While working on this I found another problem. If you have a custom AuthenticationEventPublisher bean, that bean is only picked up if you have an UserDetailsService bean.