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

Spring Security 2.7.x迁移至SecurityFilterChain时AuthenticationManager获取报错

解决Spring Security 2.7.x中获取AuthenticationManager并配置自定义过滤器的问题

核心问题

你遇到的AlreadyBuiltException是因为HttpSecurity对象调用build()后会被锁定,无法再修改。不能先调用build()获取AuthenticationManager再回头配置过滤器,必须在一次HttpSecurity配置流程中完成所有操作。

正确实现方式

以下两种常用方案,可根据场景选择:


方案1:通过@Bean暴露AuthenticationManager,供过滤器注入

适合需要在多个地方复用AuthenticationManager的场景:

  1. 定义AuthenticationManager的Bean:
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
    return authConfig.getAuthenticationManager();
}
  1. 自定义过滤器时直接注入该AuthenticationManager:
@Component
public class CustomUsernamePasswordFilter extends UsernamePasswordAuthenticationFilter {
    public CustomUsernamePasswordFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
        // 配置自定义登录请求路径
        setFilterProcessesUrl("/api/login");
    }
}
  1. 配置SecurityFilterChain并添加自定义过滤器:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http, 
                                              CustomUsernamePasswordFilter customLoginFilter,
                                              CustomBasicAuthFilter customBasicFilter,
                                              CustomAuthenticationEntryPoint entryPoint,
                                              CustomAccessDeniedHandler deniedHandler) throws Exception {
    http
        .csrf(csrf -> csrf.disable()) // 根据业务需求决定是否禁用CSRF
        .authorizeHttpRequests(auth -> auth
            .requestMatchers("/api/login").permitAll()
            .anyRequest().authenticated()
        )
        // 替换默认的UsernamePasswordAuthenticationFilter
        .addFilterAt(customLoginFilter, UsernamePasswordAuthenticationFilter.class)
        // 在BasicAuthenticationFilter之后添加自定义授权过滤器
        .addFilterAfter(customBasicFilter, BasicAuthenticationFilter.class)
        // 配置自定义错误处理器
        .exceptionHandling(ex -> ex
            .authenticationEntryPoint(entryPoint)
            .accessDeniedHandler(deniedHandler)
        );
    
    return http.build();
}

方案2:在HttpSecurity配置流程中直接获取AuthenticationManager

若不需要复用AuthenticationManager,可在配置时直接获取:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http, CustomUserDetailsService customUserDetailsService) throws Exception {
    // 先配置用户认证服务
    http.userDetailsService(customUserDetailsService);
    
    // 从HttpSecurity共享对象中获取AuthenticationManager
    AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
    
    // 实例化自定义过滤器并传入AuthenticationManager
    CustomUsernamePasswordFilter customLoginFilter = new CustomUsernamePasswordFilter(authenticationManager);
    customLoginFilter.setFilterProcessesUrl("/api/login");
    
    CustomBasicAuthFilter customBasicFilter = new CustomBasicAuthFilter(authenticationManager);
    
    // 完成HttpSecurity剩余配置
    http
        .csrf(csrf -> csrf.disable())
        .authorizeHttpRequests(auth -> auth
            .requestMatchers("/api/login").permitAll()
            .anyRequest().authenticated()
        )
        .addFilterAt(customLoginFilter, UsernamePasswordAuthenticationFilter.class)
        .addFilterAfter(customBasicFilter, BasicAuthenticationFilter.class)
        .exceptionHandling(ex -> ex
            .authenticationEntryPoint(new CustomAuthenticationEntryPoint())
            .accessDeniedHandler(new CustomAccessDeniedHandler())
        );
    
    return http.build();
}

关键注意事项

  • 必须在所有过滤器、配置项设置完成后,最后调用一次http.build()生成SecurityFilterChain,禁止中途调用build()。
  • 使用addFilterAt/addFilterBefore/addFilterAfter控制过滤器在链中的位置,确保逻辑顺序正确(比如认证过滤器要在授权过滤器之前)。

内容的提问来源于stack exchange,提问作者hotmeatballsoup

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 19:32:52