Skip to content

CSRF 保护


Spring Security 的 CSRF

Spring Security 默认启用 CSRF 保护。对于前后端分离的 API 项目,这是一个需要特别注意的点。


问题表现

java
// 加了 spring-boot-starter-security 后,不配任何东西
@PostMapping("/api/posts")
public Post create(@RequestBody Post post) {
    return postService.save(post);
}

浏览器请求这个接口时,会返回 403 Forbidden,控制台日志:

Invalid CSRF Token found for http://localhost:8080/api/posts

解决方案

API 项目:直接禁掉(最常见)

java
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())    // 禁用 CSRF
            .authorizeHttpRequests(auth -> auth
                .anyRequest().permitAll()
            );
        return http.build();
    }
}

为什么 API 项目可以禁 CSRF?

  • CSRF 攻击依赖浏览器自动携带 Cookie(会话 Cookie)
  • API 项目通常用 JWT Token 放在 Authorization 头中,不是 Cookie
  • 不存在自动携带凭证的问题,CSRF 攻击不成立

服务端渲染:保持开启

如果是传统 MVC 项目(Thymeleaf 渲染 HTML),需要 CSRF 保护:

html
<!-- Thymeleaf 模板中自动注入 CSRF Token -->
<form th:action="@{/posts}" method="post">
    <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
    <button>提交</button>
</form>

Spring Security 会自动在表单中注入 CSRF Token。


⚠️ 注意事项

1. 禁 CSRF 要放在其他配置之前

在高版本的 Spring Security 中,csrf.disable() 的调用顺序有要求。如果同时配置了其他规则,确保 csrf 配置在 authorizeHttpRequests 之前:

java
http
    .csrf(csrf -> csrf.disable())       // 先禁 CSRF
    .authorizeHttpRequests(...)          // 再配授权

2. Postman/curl 不受 CSRF 影响

CSRF 保护只对浏览器有效。curl -X POST http://localhost:8080/api/posts 不受 CSRF 影响。

3. 纯 API 项目建议统一禁用

如果不确定要不要 CSRF,记住一条规则:Token 认证(JWT)→ 禁用 CSRF;Cookie 认证(Session)→ 启用 CSRF

面向 PHP 开发者的 Spring Boot 文档