Spring Security 2.7.x迁移至SecurityFilterChain时AuthenticationManager获取报错
解决Spring Security 2.7.x中获取AuthenticationManager并配置自定义过滤器的问题
核心问题
你遇到的AlreadyBuiltException是因为HttpSecurity对象调用build()后会被锁定,无法再修改。不能先调用build()获取AuthenticationManager再回头配置过滤器,必须在一次HttpSecurity配置流程中完成所有操作。
正确实现方式
以下两种常用方案,可根据场景选择:
方案1:通过@Bean暴露AuthenticationManager,供过滤器注入
适合需要在多个地方复用AuthenticationManager的场景:
- 定义
AuthenticationManager的Bean:
@Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception { return authConfig.getAuthenticationManager(); }
- 自定义过滤器时直接注入该
AuthenticationManager:
@Component public class CustomUsernamePasswordFilter extends UsernamePasswordAuthenticationFilter { public CustomUsernamePasswordFilter(AuthenticationManager authenticationManager) { super(authenticationManager); // 配置自定义登录请求路径 setFilterProcessesUrl("/api/login"); } }
- 配置
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
相关产品推荐
相关产品推荐

