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

Spring Boot 3.2:如何将BadCredentialsException以JSON格式返回

问题描述

我有一个使用Spring Security做认证的Spring Boot服务,通过自定义AuthenticationProvider实现凭证校验,代码如下:

@Component
class CustomAuthenticationProvider(private val userService: IUserService)
: AuthenticationProvider {

 @Throws(AuthenticationException::class, UsernameNotFoundException::class, BadCredentialsException::class)
 override fun authenticate(authentication: Authentication): Authentication {
    val username: String = authentication.principal.toString()
    val password: String = authentication.credentials.toString()
    val encoder = BCryptPasswordEncoder()

    val user = userService.getUser(username) ?: throw UsernameNotFoundException("User not found")
    if (user.accountStatus == "deactivated")
        throw UsernameNotFoundException("User was deleted")
    if (!encoder.matches(password, user.password)) {
        throw BadCredentialsException("1000")
    }
    return UsernamePasswordAuthenticationToken(username, password, emptyList())
}

override fun supports(authentication: Class<*>): Boolean {
    return (authentication == UsernamePasswordAuthenticationToken::class.java)
}
}

当用户凭证不正确时,客户端收到的是Tomcat返回的403 HTML错误页面:

Error when logging in  Error transferring https:..... - server replied: 
<!doctype html><html lang="en"><head><title>HTTP Status 403 – Forbidden</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 403 – Forbidden</h1><hr class="line" /><p><b>Type</b> Status Report</p><p><b>Message</b> Access Denied</p><p><b>Description</b> The server understood the request but refuses to authorize it.</p><hr class="line" /><h3>Apache Tomcat/10.1.17</h3></body></html>

我希望把异常信息封装成JSON格式返回给客户端,但常规的控制器异常处理无法覆盖AuthenticationProvider中抛出的异常,该如何解决?

解决方案

AuthenticationProvider抛出的异常属于Spring Security认证流程异常,发生在DispatcherServlet处理请求之前,所以常规的@ControllerAdvice无法捕获,需要通过Spring Security专属的异常处理机制解决:

1. 自定义AuthenticationEntryPoint

实现AuthenticationEntryPoint接口,将异常信息封装为JSON返回:

@Component
class CustomAuthenticationEntryPoint : AuthenticationEntryPoint {
    override fun commence(
        request: HttpServletRequest,
        response: HttpServletResponse,
        authException: AuthenticationException
    ) {
        response.contentType = "application/json;charset=UTF-8"
        response.status = HttpServletResponse.SC_UNAUTHORIZED

        // 根据异常类型构造对应错误信息和编码
        val (errorCode, errorMsg) = when (authException) {
            is UsernameNotFoundException -> Pair("2001", authException.message ?: "用户不存在或已删除")
            is BadCredentialsException -> {
                val code = authException.message ?: "2002"
                val msg = if (code == "1000") "密码错误" else "凭证无效"
                Pair(code, msg)
            }
            else -> Pair("2000", "认证失败")
        }

        val result = mapOf(
            "code" to errorCode,
            "message" to errorMsg,
            "success" to false
        )

        val objectMapper = ObjectMapper()
        response.writer.write(objectMapper.writeValueAsString(result))
    }
}

2. 配置SecurityFilterChain

在Spring Security配置类中,将自定义的AuthenticationEntryPoint配置到HttpSecurity,替换默认异常处理逻辑:

@Configuration
@EnableWebSecurity
class SecurityConfig(
    private val customAuthenticationProvider: CustomAuthenticationProvider,
    private val customAuthenticationEntryPoint: CustomAuthenticationEntryPoint
) {
    @Bean
    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
        http
            .authorizeHttpRequests { auth ->
                auth.anyRequest().authenticated()
            }
            .formLogin { form ->
                // 表单登录场景下,指定登录失败后的异常处理
                form.failureHandler { request, response, exception ->
                    customAuthenticationEntryPoint.commence(request, response, exception)
                }
            }
            .authenticationProvider(customAuthenticationProvider)
            .exceptionHandling { exception ->
                // 配置全局认证异常处理入口
                exception.authenticationEntryPoint(customAuthenticationEntryPoint)
            }
            .csrf { it.disable() } // 根据业务需求决定是否关闭CSRF

        return http.build()
    }
}

补充说明

  • 若使用HTTP Basic或其他认证方式,需在对应配置中指定failureHandler,或通过exceptionHandling全局配置authenticationEntryPoint。
  • 可根据业务需求扩展异常类型判断,返回更精准的错误信息。
  • 确保项目已引入Jackson依赖(Spring Boot Web Starter默认包含),用于JSON序列化。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 18:20:29