如何替换Spring Security默认的RememberMeAuthenticationProvider
问题
我需要自定义实现RememberMeAuthenticationProvider,且已掌握相关方法,但默认的实例仍存在于ProviderManager的提供者列表中。能否用自定义实例完全替换默认的?
以下是我添加自定义提供者的代码:
@Bean public RememberMeAuthenticationProvider rememberMeAuthenticationProvider(MessageSource messageSource) { var authProvider = new MyRememberMeAuthenticationProvider(REMEMBER_ME_KEY); authProvider.setMessageSource(messageSource); return authProvider; } @Bean public SecurityFilterChain filterChain(HttpSecurity http, MvcRequestMatcher.Builder mvc, MessageSource messageSource) throws Exception { http .csrf(csrf -> csrf.csrfTokenRequestHandler(csrfRequestHandler) .ignoringRequestMatchers(LOGIN_URI + "**")) .authenticationProvider(authenticationProvider) .authenticationProvider(rememberMeAuthenticationProvider(messageSource)) <cut> return http.build(); }
但RememberMeConfigurer会无条件添加默认的提供者:
@Override public void configure(H http) { RememberMeAuthenticationFilter rememberMeFilter = new RememberMeAuthenticationFilter( http.getSharedObject(AuthenticationManager.class), this.rememberMeServices); if (this.authenticationSuccessHandler != null) { rememberMeFilter.setAuthenticationSuccessHandler(this.authenticationSuccessHandler); } SecurityContextConfigurer<?> securityContextConfigurer = http.getConfigurer(SecurityContextConfigurer.class); if (securityContextConfigurer != null && securityContextConfigurer.isRequireExplicitSave()) { SecurityContextRepository securityContextRepository = securityContextConfigurer .getSecurityContextRepository(); rememberMeFilter.setSecurityContextRepository(securityContextRepository); } rememberMeFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy()); rememberMeFilter = postProcess(rememberMeFilter); http.addFilter(rememberMeFilter); }
这导致配置的提供者列表如下:
0 = {MyAuthenticationProvider@17013} 1 = {MyRememberMeAuthenticationProvider@16992} 2 = {AnonymousAuthenticationProvider@26592} 3 = {RememberMeAuthenticationProvider@18312}
目前虽因顺序问题能正常工作,但我希望彻底移除默认的提供者,请问是否可行?
解决方案
完全可以移除默认的RememberMeAuthenticationProvider并替换为自定义实现,以下是几种可靠的实现方式:
1. 用@Primary覆盖默认Bean
Spring Security会优先使用标记了@Primary的同名Bean,直接在你的自定义提供者Bean上添加该注解,就能强制框架使用你的实现而非默认实例:
@Bean @Primary public RememberMeAuthenticationProvider rememberMeAuthenticationProvider(MessageSource messageSource) { var authProvider = new MyRememberMeAuthenticationProvider(REMEMBER_ME_KEY); authProvider.setMessageSource(messageSource); return authProvider; }
这种方式最简洁,符合Spring的依赖注入设计原则,推荐优先使用。
2. 自定义AuthenticationManager手动指定提供者
绕过框架自动配置的ProviderManager,自己构建只包含所需认证提供者的实例,从根源上避免默认实例被添加:
@Bean public AuthenticationManager authenticationManager(MyAuthenticationProvider myAuthProvider, RememberMeAuthenticationProvider customRememberMeProvider) { ProviderManager providerManager = new ProviderManager(Arrays.asList( myAuthProvider, customRememberMeProvider, new AnonymousAuthenticationProvider("anonymous-key") // 按需保留匿名认证提供者 )); // 禁用父级认证管理器,防止默认提供者被继承添加 providerManager.setParentAuthenticationManager(null); return providerManager; }
之后在SecurityFilterChain中指定使用这个自定义的认证管理器:
@Bean public SecurityFilterChain filterChain(HttpSecurity http, MvcRequestMatcher.Builder mvc, AuthenticationManager customAuthManager) throws Exception { http .csrf(csrf -> csrf.csrfTokenRequestHandler(csrfRequestHandler) .ignoringRequestMatchers(LOGIN_URI + "**")) .authenticationManager(customAuthManager) <cut> return http.build(); }
3. 后置处理移除默认提供者
如果默认实例已经被注册,可以通过@PostConstruct方法获取ProviderManager并手动移除不需要的提供者(注意:反射操作需谨慎,框架版本更新可能导致失效):
@Autowired private AuthenticationManager authenticationManager; @PostConstruct public void removeDefaultRememberMeProvider() { if (authenticationManager instanceof ProviderManager pm) { List<AuthenticationProvider> filteredProviders = pm.getProviders().stream() .filter(p -> !(p instanceof RememberMeAuthenticationProvider && !(p instanceof MyRememberMeAuthenticationProvider))) .collect(Collectors.toList()); try { Field providersField = ProviderManager.class.getDeclaredField("providers"); providersField.setAccessible(true); providersField.set(pm, Collections.unmodifiableList(filteredProviders)); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException("Failed to remove default RememberMeAuthenticationProvider", e); } } }
内容的提问来源于stack exchange,提问作者Micke
相关产品推荐
相关产品推荐

