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

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注入

  1. 在Security配置类中暴露AuthenticationManager Bean,并手动创建自定义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);
    }
}
  1. 自定义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设计规范:

  1. 自定义成功处理器
@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);
    }
}
  1. 自定义失败处理器
@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);
    }
}
  1. 在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 10:13:22