在 Spring Boot 项目中引入 Spring Security 后,框架默认会通过 CsrfFilter 对状态变更的 HTTP 请求进行防护。EnableCSRF 并不是某一个单独注解,而是 Spring Security 配置体系中用于开启 CSRF 防御能力的统称,通常体现在 SecurityFilterChain 里通过 csrf() 配置段来启用。理解它的运作方式,是避免误关防护或错误放行接口的前提。CSRF 攻击利用浏览器自动携带 Cookie 的特性,诱导用户在已登录状态下访问恶意页面并发起非本意请求,而 Spring Security 的令牌机制能有效阻断这类伪造行为。

CSRF 防护的底层原理与过滤器链位置
Spring Security 的防护核心由 CsrfFilter 承担,它位于过滤器链的中后部,专门拦截 POST、PUT、DELETE 等可能修改服务端状态的请求。每次请求进入时,CsrfFilter 会从请求头或请求参数中提取名为 _csrf 的令牌,并与服务端保存在 HttpSession 中的令牌进行比对。如果令牌缺失或校验失败,过滤器会直接返回 403 禁止访问,从而阻止伪造请求生效。这种机制依赖于同源策略和会话状态,因此对传统服务端渲染页面非常友好。
在 Spring Boot 自动装配场景下,只要引入了 spring-boot-starter-security 且未显式禁用,WebSecurityConfigurerAdapter 的默认实现就会注册 CsrfFilter。开发者常误以为 @EnableWebSecurity 本身开启了 CSRF,其实该注解仅用于加载安全配置,真正的开关在 http.csrf().disable() 是否被调用。若未调用 disable,CSRF 默认处于启用状态,这也是很多初学者在提交表单时突然遇到 403 的原因。
令牌的生成由 CsrfTokenRepository 接口负责,默认实现 HttpSessionCsrfTokenRepository 将令牌存入会话并写入响应。对于前后端分离项目,常用 CookieCsrfTokenRepository 将令牌放到 Cookie 中,前端通过 JS 读取后放入请求头 X-CSRF-TOKEN。下面是一段典型的仓库配置代码:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
)
.authorizeHttpRequests(auth -> auth
.anyRequest().authenticated()
);
return http.build();
}
}
表单提交与 Ajax 请求中的令牌处理方案
在传统 Thymeleaf 或 JSP 页面中,Spring Security 提供了标签支持自动注入隐藏域。使用 Thymeleaf 时只需在表单内加入 <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>,提交时参数会自动带上令牌。服务端 CsrfFilter 从参数中读取并完成校验,开发者几乎无感知。这种方式适合页面由后端渲染、Cookie 同源的场景,安全性与易用性平衡得较好。
当项目采用 Vue 或 React 等前端框架调用后端 REST 接口时,浏览器不会自动把 Cookie 里的令牌塞进请求头。此时前端需要在首次获取页面或登录后,从 Cookie 中读取 XSRF-TOKEN 并配置到 Axios 的默认头中。例如设置 axios.defaults.headers.common['X-CSRF-TOKEN'] = getCookie('XSRF-TOKEN'),后端 CookieCsrfTokenRepository 默认会从 X-CSRF-TOKEN 头读取令牌。若前端遗漏这一步,所有写请求都会因校验失败被拦截。
部分团队为了省事直接在配置中 http.csrf().disable(),这等于拆掉了大门。正确做法应是区分接口类型:对公开查询接口可忽略,对写接口强制校验。Spring Security 允许通过 requestMatchers 精确控制,示例如下:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf
.ignoringRequestMatchers("/api/public/**")
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.anyRequest().authenticated()
);
return http.build();
}
常见误区与自定义忽略规则的安全边界
一个广泛存在的误区是认为 RESTful 接口无状态就不需要 CSRF。事实上只要使用 Cookie 做认证,浏览器就会自动发送凭证,攻击者可借用户浏览器发起伪造请求,因此基于 Cookie 的 JWT 同样面临风险。只有采用 Authorization 头携带 Token 且前端不自动附加凭证的方案,才可能天然免疫 CSRF,但这类方案需配合 CORS 严格限制。
另一个问题是滥用 ignoringRequestMatchers 导致防护形同虚设。曾有团队把 /api/** 全部忽略,仅保留登录页防护,结果写操作完全暴露。安全边界应缩小到真正无需防护的匿名接口,例如短信验证码发送、公开搜索等。对于内部管理端,建议保持全量校验,并通过前端统一拦截器注入令牌,降低人为遗漏概率。
若项目必须兼容老客户端不支持令牌,可考虑双重提交 Cookie 模式或自定义 CsrfTokenRepository 做白名单域名校验,但复杂度明显上升。下表对比了三种常见策略的适用场景:
| 策略 | 安全性 | 改造量 | 适用场景 |
|---|---|---|---|
| 默认 Session 令牌 | 高 | 低 | 服务端渲染系统 |
| Cookie 令牌加头 | 高 | 中 | 前后端分离同源 |
| 全局 disable | 无 | 零 | 纯无 Cookie 鉴权 |
综合来看,EnableCSRF 相关配置不是简单开关,而是需要结合认证方式、前端架构与接口公开程度来设计。正确使用 CookieCsrfTokenRepository 并约束忽略范围,能在不牺牲用户体验的前提下守住安全底线。
Spring_BootSpring_SecurityCSRF修改时间:2026-08-16 04:48:33