在Spring Boot集成Spring Security的认证体系中,默认的认证异常会返回统一的错误响应,无法区分具体的异常原因,比如无法明确告知前端是账号不存在还是密码错误。要实现自定义认证异常并返回精确错误消息,需要从异常定义、处理器编写、配置整合三个层面逐步完成。

自定义认证异常类
首先需要定义不同的自定义认证异常,每个异常对应一种具体的认证失败场景,并且携带对应的错误消息和错误码。
// 基础认证异常父类
public class CustomAuthException extends RuntimeException {
private String errorCode;
private String errorMsg;
public CustomAuthException(String errorCode, String errorMsg) {
super(errorMsg);
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
// getter方法
public String getErrorCode() {
return errorCode;
}
public String getErrorMsg() {
return errorMsg;
}
}
// 账号不存在异常
public class AccountNotFoundException extends CustomAuthException {
public AccountNotFoundException() {
super("AUTH_001", "账号不存在");
}
}
// 密码错误异常
public class PasswordWrongException extends CustomAuthException {
public PasswordWrongException() {
super("AUTH_002", "密码错误");
}
}
// 令牌过期异常
public class TokenExpiredException extends CustomAuthException {
public TokenExpiredException() {
super("AUTH_003", "认证令牌已过期");
}
}
编写认证异常处理器
认证异常处理器需要捕获自定义的认证异常,然后将异常中的错误码和错误消息封装成统一的响应格式返回给前端。
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@Component
public class CustomAuthExceptionHandler implements AuthenticationEntryPoint {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
Map<String, Object> result = new HashMap<>();
// 判断是否为自定义认证异常
if (authException.getCause() instanceof CustomAuthException) {
CustomAuthException customException = (CustomAuthException) authException.getCause();
result.put("code", customException.getErrorCode());
result.put("msg", customException.getErrorMsg());
} else {
// 非自定义异常返回默认提示
result.put("code", "AUTH_999");
result.put("msg", "认证失败,请检查请求参数");
}
result.put("data", null);
// 写入响应
response.getWriter().write(objectMapper.writeValueAsString(result));
}
}
在认证逻辑中抛出自定义异常
需要在自定义的认证逻辑中,根据不同的失败场景抛出对应的自定义认证异常,这样异常处理器才能捕获到具体的异常类型。
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component;
import java.util.Collections;
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
// 模拟账号校验逻辑
if (!"admin".equals(username)) {
// 抛出账号不存在自定义异常
throw new AccountNotFoundException();
}
// 模拟密码校验逻辑
if (!"123456".equals(password)) {
// 抛出密码错误自定义异常
throw new PasswordWrongException();
}
// 认证通过返回认证信息
return new UsernamePasswordAuthenticationToken(username, password, Collections.emptyList());
}
@Override
public boolean supports(Class<?> authentication) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
}
整合配置到Spring Security
最后需要将自定义异常处理器和自定义认证提供者整合到Spring Security的配置中,让整个认证流程生效。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomAuthExceptionHandler customAuthExceptionHandler;
@Autowired
private CustomAuthenticationProvider customAuthenticationProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 设置自定义认证提供者
auth.authenticationProvider(customAuthenticationProvider);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
// 配置认证异常处理入口
.exceptionHandling()
.authenticationEntryPoint(customAuthExceptionHandler);
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
测试验证
完成上述配置后,可以通过不同的请求场景验证错误消息是否精确返回:
- 请求/login接口时传入不存在的账号,会返回
{"code":"AUTH_001","msg":"账号不存在","data":null} - 请求/login接口时传入错误密码,会返回
{"code":"AUTH_002","msg":"密码错误","data":null} - 请求需要认证的接口时携带过期令牌,会返回
{"code":"AUTH_003","msg":"认证令牌已过期","data":null}
整个流程中需要注意自定义异常需要被包装到AuthenticationException的cause中,否则AuthenticationEntryPoint无法正确捕获。如果项目中使用了JWT令牌认证,只需要在令牌校验的逻辑中抛出对应的TokenExpiredException等自定义异常即可,处理逻辑和账号密码认证一致。
Spring_Boot自定义认证异常错误消息返回认证异常处理Spring_Security修改时间:2026-07-14 19:12:33