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,再与业务异常处理器分工协作。

笔记加载中…