SpringBoot HttpBasic认证成败处理器实现及启动报错解决
为HttpBasic认证添加自定义成功/失败处理器
问题场景
需要为HttpBasic认证添加自定义逻辑:记录认证失败尝试次数,或在登录成功时重置失败次数。尝试通过自定义BasicAuthenticationFilter实现,但启动时抛出错误:
Parameter 0 of constructor in com.shabodi.laas.config.CustomBasicAuthFilter required a bean of type 'org.springframework.security.authentication.AuthenticationManager' that could not be found.
Consider defining a bean of type 'org.springframework.security.authentication.AuthenticationManager' in your configuration.
错误原因
Spring Security默认不会将AuthenticationManager注册为全局Bean,直接通过@Autowired注入会找不到对应实例。
解决方案
方案一:修复自定义Filter的AuthenticationManager注入
- 在Security配置类中暴露
AuthenticationManagerBean,并手动创建自定义Filter实例
@Configuration @EnableWebSecurity public class SpringSecurityConfig { @Autowired @Qualifier("customAuthenticationEntryPoint") AuthenticationEntryPoint authEntryPoint; // 暴露AuthenticationManager全局Bean @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception { return authConfig.getAuthenticationManager(); } @Bean @Order(Ordered.HIGHEST_PRECEDENCE + 5) public SecurityFilterChain initSetupSecurityFilterChain(HttpSecurity http) throws Exception { http .securityMatcher(AntPathRequestMatcher.antMatcher("/init-setup/**")) .authorizeHttpRequests(authorizeRequests -> authorizeRequests .requestMatchers(HttpMethod.POST, "/init-setup/otp").permitAll() .requestMatchers(HttpMethod.GET, "/init-setup/default-client-credentials").authenticated() .anyRequest().authenticated() ) .csrf(AbstractHttpConfigurer::disable) .sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .addFilterAt(customBasicAuthFilter(), BasicAuthenticationFilter.class) .exceptionHandling(Customizer.withDefaults()); return http.build(); } // 手动创建自定义Filter,避免Autowired注入问题 @Bean("customBasicAuthFilter") public BasicAuthenticationFilter customBasicAuthFilter(AuthenticationManager authenticationManager) { return new CustomBasicAuthFilter(authenticationManager); } }
- 自定义Filter添加业务逻辑(示例:注入服务记录/重置次数)
public class CustomBasicAuthFilter extends BasicAuthenticationFilter { // 注入自定义业务服务,用于处理失败次数记录 private final AuthAttemptRecordService authAttemptRecordService; public CustomBasicAuthFilter(AuthenticationManager authenticationManager, AuthAttemptRecordService authAttemptRecordService) { super(authenticationManager); this.authAttemptRecordService = authAttemptRecordService; } @Override protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) throws IOException { // 登录成功:重置该用户的失败尝试次数 String username = authResult.getName(); authAttemptRecordService.resetFailedAttempts(username); super.onSuccessfulAuthentication(request, response, authResult); } @Override protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException { // 登录失败:记录该用户的失败尝试次数 if (failed instanceof BadCredentialsException) { String username = ((BadCredentialsException) failed).getAuthentication().getName(); authAttemptRecordService.recordFailedAttempt(username); } super.onUnsuccessfulAuthentication(request, response, failed); } }
方案二:直接使用HttpBasicConfigurer配置处理器(更简洁)
无需替换原有Filter,直接通过httpBasic()的配置项绑定成功/失败处理器,更符合Spring Security设计规范:
- 自定义成功处理器
@Component public class CustomBasicSuccessHandler implements AuthenticationSuccessHandler { private final AuthAttemptRecordService authAttemptRecordService; public CustomBasicSuccessHandler(AuthAttemptRecordService authAttemptRecordService) { this.authAttemptRecordService = authAttemptRecordService; } @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { // 重置失败次数 String username = authentication.getName(); authAttemptRecordService.resetFailedAttempts(username); // 保留HttpBasic默认成功响应 response.setStatus(HttpServletResponse.SC_OK); } }
- 自定义失败处理器
@Component public class CustomBasicFailureHandler implements AuthenticationFailureHandler { private final AuthAttemptRecordService authAttemptRecordService; public CustomBasicFailureHandler(AuthAttemptRecordService authAttemptRecordService) { this.authAttemptRecordService = authAttemptRecordService; } @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { // 记录失败次数 if (exception instanceof BadCredentialsException) { String username = ((BadCredentialsException) exception).getAuthentication().getName(); authAttemptRecordService.recordFailedAttempt(username); } // 保留HttpBasic默认失败响应(返回401) response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); } }
- 在Security配置中绑定处理器
@Configuration @EnableWebSecurity public class SpringSecurityConfig { @Autowired @Qualifier("customAuthenticationEntryPoint") AuthenticationEntryPoint authEntryPoint; @Autowired private CustomBasicSuccessHandler customBasicSuccessHandler; @Autowired private CustomBasicFailureHandler customBasicFailureHandler; @Bean @Order(Ordered.HIGHEST_PRECEDENCE + 5) public SecurityFilterChain initSetupSecurityFilterChain(HttpSecurity http) throws Exception { http .securityMatcher(AntPathRequestMatcher.antMatcher("/init-setup/**")) .authorizeHttpRequests(authorizeRequests -> authorizeRequests .requestMatchers(HttpMethod.POST, "/init-setup/otp").permitAll() .requestMatchers(HttpMethod.GET, "/init-setup/default-client-credentials").authenticated() .anyRequest().authenticated() ) .csrf(AbstractHttpConfigurer::disable) .sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .httpBasic(basic -> basic .authenticationEntryPoint(authEntryPoint) .successHandler(customBasicSuccessHandler) .failureHandler(customBasicFailureHandler) ) .exceptionHandling(Customizer.withDefaults()); return http.build(); } }
注意事项
- 需确保自定义的
AuthAttemptRecordService(业务服务)已注册为Spring Bean,实现基于用户名的失败次数存储(如Redis、数据库)与重置逻辑。 - 方案二更轻量化,无需修改原有Filter链,推荐优先使用。
内容的提问来源于stack exchange,提问作者Shaun Serrao
相关产品推荐
相关产品推荐

