You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何替换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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 09:25:15