🤔 为什么要统一异常返回?
Spring Security 默认的异常返回行为:
- 登录失败?返回一堆英文堆栈;
- 没权限访问?直接 403 Forbidden,JSON 都不是;
- Token 失效?跳转登录页;
- 接口出错?前端无法处理异常信息……
对于前后端分离项目来说,简直是灾难!
我们希望:
- ✅ 所有异常统一返回 JSON;
- ✅ 状态码清晰(401、403、500);
- ✅ 响应格式统一,前端易处理;
- ✅ 错误信息可自定义提示,方便用户与开发定位问题。
🧱 一、定义统一响应结构
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ApiResponse<T> {
private int code;
private String message;
private T data;
public static <T> ApiResponse<T> fail(int code, String message) {
return new ApiResponse<>(code, message, null);
}
}
🔐 二、自定义异常处理组件
1️⃣ 未登录:认证失败处理器
实现 AuthenticationEntryPoint 接口:
@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
ApiResponse<?> result = ApiResponse.fail(401, "未登录或登录过期,请重新登录");
response.getWriter().write(new ObjectMapper().writeValueAsString(result));
}
}
2️⃣ 权限不足:访问被拒绝处理器
实现 AccessDeniedHandler 接口:
@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request,
HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException {
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
ApiResponse<?> result = ApiResponse.fail(403, "权限不足,无法访问此资源");
response.getWriter().write(new ObjectMapper().writeValueAsString(result));
}
}
🔧 三、注册到 Security 配置中
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http,
CustomAuthenticationEntryPoint entryPoint,
CustomAccessDeniedHandler accessDeniedHandler) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/login", "/public/**").permitAll()
.anyRequest().authenticated()
)
.exceptionHandling(exception -> exception
.authenticationEntryPoint(entryPoint)
.accessDeniedHandler(accessDeniedHandler)
);
return http.build();
}
}
✅ 效果验证
| 场景 | HTTP 状态码 | JSON 响应内容 |
|---|---|---|
| 未登录访问 | 401 | {"code":401,"message":"未登录或登录过期"} |
| 权限不足 | 403 | {"code":403,"message":"权限不足"} |
| 登录失败(扩展) | 401 | {"code":401,"message":"用户名或密码错误"} |
✍️ 总结
通过实现:
AuthenticationEntryPoint→ 处理未登录异常AccessDeniedHandler→ 处理权限不足异常- 配置进
HttpSecurity的异常处理链路
你就可以实现一套 规范、前后端友好、可维护性强 的 Spring Security 异常返回机制。