SpringBoot2.7 OAuth2资源服务器AuthenticationEventPublisher失效
此前基于Spring Boot 2.6编写OAuth2资源服务器安全配置时,通过继承WebSecurityConfigurerAdapter实现JWT校验逻辑,运行正常,原配置代码如下:
@Configuration @EnableWebSecurity public class ResourceServerConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() . . . .and() .oauth2ResourceServer() .jwt(); } }
升级至Spring Boot 2.7后,按照官方推荐方案,使用标注@Bean、返回SecurityFilterChain的方法替代已废弃的WebSecurityConfigurerAdapter进行配置,发现应用中AuthenticationEventPublisher不再正常工作,升级后的配置代码如下:
@Configuration @EnableWebSecurity public class ResourceServerConfig { @Bean public SecurityFilterChain configure(HttpSecurity http) throws Exception { http .authorizeRequests() . . . .and() .oauth2ResourceServer() .jwt(); return http.build(); } }
问题根源已经定位:BearerTokenAuthenticationFilter始终采用ProviderManager作为AuthenticationManager实现,而ProviderManager默认将eventPublisher属性初始化为NullEventPublisher,这个空实现不会发布任何认证事件,相关源码片段如下:
public class ProviderManager implements AuthenticationManager, . . . { . . . private AuthenticationEventPublisher eventPublisher = new NullEventPublisher();
在原先使用WebSecurityConfigurerAdapter的场景下,框架会自动为ProviderManager注入DefaultAuthenticationEventPublisher的有效实例,因此认证事件可正常发布。
测试找到的临时修复方案分两步:
- 编写全局配置类生成自定义
ProviderManagerBean,注入JwtAuthenticationProvider并为其设置Spring容器中存在的AuthenticationEventPublisher实例,配置代码如下:
@Configuration @ConditionalOnClass({AuthenticationEventPublisher.class, JwtAuthenticationProvider.class}) public class SpringConfiguration { //global configuration for several others @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; } }
- 在各应用的安全配置中注入该
ProviderManager,通过oauth2ResourceServer().jwt().authenticationManager(manager)方法显式指定认证管理器,即可恢复事件发布能力,调整后的安全配置如下:
@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(); } }
这个方案存在两个明显问题:
- 开发的模块是供公司内部数十个应用依赖的通用组件,用于统一发布特定日志,该方案要求所有接入应用都手动添加注入
ProviderManager、显式配置认证管理器的代码,接入改造成本过高。 - 无法确认强制为所有接入应用预置自定义
ProviderManager会带来哪些潜在兼容性风险。
是否存在可行方案,能够绕过ProviderManager默认初始化NullEventPublisher的逻辑,无需所有配置SecurityFilterChain的应用都手动调用oauth2ResourceServer().authenticationManager(manager)指定认证管理器,即可让AuthenticationEventPublisher正常发布认证事件?
直接在通用组件里注册一个BeanPostProcessor,在Spring容器初始化BearerTokenAuthenticationFilter实例完成后,取出它内部持有的ProviderManager,把容器里的AuthenticationEventPublisher设置进去即可。全程不需要业务应用做任何代码改动,也不会替换全局默认的ProviderManager实例,没有额外兼容性风险。
实现代码如下:
@Bean public BeanPostProcessor bearerTokenFilterEventPublisherConfigurer(AuthenticationEventPublisher eventPublisher) { return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof BearerTokenAuthenticationFilter filter) { AuthenticationManager authManager = filter.getAuthenticationManager(); if (authManager instanceof ProviderManager providerManager) { providerManager.setAuthenticationEventPublisher(eventPublisher); } } return bean; } }; }
这个方案的优势很明确:
- 不修改框架原有创建
ProviderManager、BearerTokenAuthenticationFilter的流程,完全复用Spring Security默认的自动配置逻辑,不会和其他自定义安全配置产生冲突。 - 只针对资源服务器用到的
BearerTokenAuthenticationFilter持有的认证管理器做事件发布器注入,影响范围可控,不会干扰其他认证链路(比如表单登录、短信登录等场景用到的独立ProviderManager实例)。 - 配置放在通用组件里,业务应用引入依赖后自动生效,零接入成本。
如果需要覆盖所有Spring Security自动创建的ProviderManager实例(不只是OAuth2资源服务器场景),直接把判断逻辑改成匹配ProviderManager类型即可,同样不需要业务侧做任何改动。
内容的提问来源于stack exchange,提问作者André Campanini

