Security 中的 CSRF、CORS 与异常处理
安全落地时必碰三件事:要不要防 CSRF、跨域怎么放行、认证/授权失败怎么返回统一 JSON。本章以 Security 6 / Boot 3 为主线给结论与代码。
CSRF:按认证方式取舍
CSRF(跨站请求伪造)指攻击者诱导已登录用户浏览器,向目标站自动发出改状态请求。Security 默认启用防护:对 POST/PUT/DELETE 要求携带令牌,GET 等只读请求不受影响。
- 表单登录 + Cookie 会话:必须保留 CSRF 防护(表单里注入令牌);
- 纯 API +
Authorization头(JWT,第 31 章):浏览器不会自动带自定义头,攻击者无法伪造,通常全局关闭:
http.csrf(AbstractHttpConfigurer::disable);
CORS:两层都要配
CORS(跨源资源共享)由浏览器策略触发。Spring 侧先配好"允许谁":
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration cfg = new CorsConfiguration();
cfg.setAllowedOrigins(List.of("https://example.com")); // 白名单;带凭据时不能用 *
cfg.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
cfg.setAllowedHeaders(List.of("*"));
cfg.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", cfg);
return source;
}
再让 Security 链接入它(否则被安全过滤器挡在前面):
http.cors(Customizer.withDefaults())
.csrf(AbstractHttpConfigurer::disable);
预检请求 OPTIONS 由 CorsFilter 直接应答。小范围临时放行也可用控制器注解 @CrossOrigin(origins = "https://example.com"),生产建议用上面的全局白名单而非 *。
401 与 403 的统一 JSON
安全过滤器链中的失败由 ExceptionTranslationFilter 处理,两个回调分别对应"未认证"与"已认证但无权限":
http.exceptionHandling(e -> e
.authenticationEntryPoint((req, res, ex) -> { // 未认证 → 401
res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
res.setContentType("application/json;charset=UTF-8");
res.getWriter().write("{\"code\":401,\"message\":\"未认证或令牌失效\"}");
})
.accessDeniedHandler((req, res, ex) -> { // 无权限 → 403
res.setStatus(HttpServletResponse.SC_FORBIDDEN);
res.setContentType("application/json;charset=UTF-8");
res.getWriter().write("{\"code\":403,\"message\":\"没有访问权限\"}");
}));
两个默认行为要知道:未配表单登录/入口时,匿名访问保护资源默认返回 403(Http403ForbiddenEntryPoint),很多"为什么没登录却是 403"的困惑来自这里;自定义了 authenticationEntryPoint 后才会返回 401。响应体建议与全局返回体(第 45 章 Result)保持一致,用 ObjectMapper 序列化统一结构。
与 @RestControllerAdvice 的分工
- 安全链内的 401/403 → 上面两个 Handler;
- Controller 里抛出的业务/校验异常 → @RestControllerAdvice(第 44 章)处理;
- 两者互补,别混用。
小结:JWT 无状态接口通常关闭 CSRF、用白名单开 CORS;401/403 由 exceptionHandling 的两个回调输出统一 JSON,再与业务异常处理器分工协作。