Spring Boot 3添加JWT过滤器至过滤链报‘已构建’错误求解
Spring Boot 3 解决SecurityFilterChain与AuthenticationManager构建冲突问题
问题描述
配置Spring Security时,添加JWT过滤器后触发异常:
Factory method 'filterChain' threw exception with message: This object has already been built
移除JWT过滤器后系统正常运行,原因是AuthenticationManager构建时已触发HttpSecurity对象的build操作,后续filterChain方法再次调用http.build()就会触发冲突。
原有配置代码
SecurityFilterChain 配置方法
@Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf().disable() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .addFilter(new JwtUserNameAndPasswordAuthFilter(authManager(http))) .authorizeHttpRequests() .requestMatchers("/", "/index.html", "/css/*", "/js/*").permitAll() .requestMatchers("/api/**").hasRole(STUDENT.name()) .anyRequest().authenticated(); return http.build(); }
AuthenticationManager 配置方法
@Bean public AuthenticationManager authManager(HttpSecurity http) throws Exception { AuthenticationManagerBuilder authenticationManagerBuilder = http .getSharedObject(AuthenticationManagerBuilder.class); authenticationManagerBuilder.authenticationProvider(daoAuthenticationProvider()); return authenticationManagerBuilder.build(); }
解决方案
方案一:直接在HttpSecurity链中配置AuthenticationProvider
去掉单独的authManager Bean,直接将AuthenticationProvider配置到HttpSecurity的链式调用中,让Spring Security自动处理AuthenticationManager的创建,避免提前触发HttpSecurity的build操作:
@Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf().disable() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authenticationProvider(daoAuthenticationProvider()) // 直接注入自定义Provider .addFilter(new JwtUserNameAndPasswordAuthFilter(http.getSharedObject(AuthenticationManager.class))) .authorizeHttpRequests() .requestMatchers("/", "/index.html", "/css/*", "/js/*").permitAll() .requestMatchers("/api/**").hasRole(STUDENT.name()) .anyRequest().authenticated(); return http.build(); } // 保留自定义AuthenticationProvider的Bean配置 @Bean public DaoAuthenticationProvider daoAuthenticationProvider() { DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); // 配置UserDetailsService和PasswordEncoder等 provider.setUserDetailsService(userDetailsService()); provider.setPasswordEncoder(passwordEncoder()); return provider; }
方案二:通过AuthenticationConfiguration获取AuthenticationManager
如果需要保留独立的AuthenticationManager Bean供其他地方使用,不要从HttpSecurity中手动构建,而是通过AuthenticationConfiguration获取:
@Bean public AuthenticationManager authManager(AuthenticationConfiguration authConfig) throws Exception { return authConfig.getAuthenticationManager(); } @Bean public SecurityFilterChain filterChain(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception { http .csrf().disable() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .addFilter(new JwtUserNameAndPasswordAuthFilter(authenticationManager)) // 注入已构建好的AuthenticationManager .authorizeHttpRequests() .requestMatchers("/", "/index.html", "/css/*", "/js/*").permitAll() .requestMatchers("/api/**").hasRole(STUDENT.name()) .anyRequest().authenticated(); return http.build(); } @Bean public DaoAuthenticationProvider daoAuthenticationProvider() { DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); provider.setUserDetailsService(userDetailsService()); provider.setPasswordEncoder(passwordEncoder()); return provider; }
原理说明
- 方案一利用Spring Security的链式配置特性,将
AuthenticationProvider直接绑定到HttpSecurity,在最终调用http.build()时才统一构建AuthenticationManager和SecurityFilterChain,避免了重复构建的冲突。 - 方案二通过
AuthenticationConfiguration获取全局的AuthenticationManager,由Spring统一管理其生命周期,不会和HttpSecurity的构建流程产生交叉冲突。
内容的提问来源于stack exchange,提问作者derstauner
相关产品推荐
相关产品推荐

