导读:本期聚焦于阿亮创作的《Spring Boot 如何整合 Spring Security 实现 CSRF 防护?》,敬请观看详情。CSRF 攻击的核心风险在于浏览器会自动携带目标域下的 Cookie,攻击者利用这个特性在第三方页面构造请求,让已登录用户无感知地执行敏感操作。Spring Boot 项目通常依赖 Spring Security 提供的内置 CSRF 防护,但默认配置只适用于传统的服务端渲染页面,一旦切换到前后端分离架构,就需要自定义 Token 存储与传递方式。本文从 CSRF 攻击原理出发,详细拆解 Spring Security 中 CsrfFilter 的工作流程,说明如何通过 CookieCsrfTokenRepository 将 token 写入 Cookie,并与前端 Axios 拦截器配合在请求头中回传 X-XSRF-TOKEN。同时演示了完整的 Java 配置代码和测试方法,对比了禁用 CSRF、仅使用 SameSite Cookie 等方案的实际差异,帮助读者根据项目形态选择最合适的防护策略,避免因配置不当导致安全漏洞。

CSRF(Cross-Site Request Forgery,跨站请求伪造)是一种利用已登录用户身份在非本人意愿下发送请求的攻击方式。攻击者通过诱导用户访问恶意页面或点击恶意链接,借用浏览器自动携带 Cookie 的特性,伪造用户操作请求。Spring Boot 项目中通常集成 Spring Security 来获得开箱即用的 CSRF 防护能力,但实际配置中需要根据前后端架构灵活调整。本文将分别介绍 CSRF 攻击原理、Spring Security 默认防护机制、前后端分离场景下的自定义实现以及测试验证方法,帮助开发者构建安全的 Web 应用。

Spring Boot 如何整合 Spring Security 实现 CSRF 防护?

CSRF 攻击原理与危害

CSRF 攻击成立的前提是目标网站使用 Cookie 作为身份凭证,并且浏览器在发起跨站请求时会自动携带目标域下的 Cookie。举个例子,用户登录了网银系统,浏览器保存了包含会话标识的 Cookie。此时用户又访问了一个恶意网站,该网站中有一个自动提交的表单,表单的 action 指向网银的转账接口,参数为收款人账号和金额。由于恶意页面加载时会自动提交这个表单,而浏览器会携带目标域(网银)的 Cookie 发送请求,最终导致转账操作在用户不知情的情况下被执行。

CSRF 与 XSS 有本质区别。XSS 是攻击者把恶意脚本注入到目标网站页面中执行,从而窃取数据或篡改页面;而 CSRF 并不需要攻击者拿到 Cookie 内容,它只是诱导浏览器向目标网站发起请求,利用的是浏览器自动携带 Cookie 的机制。因此,即使 Cookie 设置了 HttpOnly 防止 JavaScript 读取,CSRF 攻击依然可以成功。常见的防护思路包括验证 Referer 或 Origin 头、使用验证码、引入一次性 Token 校验等。Spring Security 采用的是同步令牌模式,即服务端生成随机 Token,请求必须携带该 Token 才能通过校验。

从攻击流程可以看出,CSRF 的破坏力取决于目标接口的敏感程度。修改密码、转账、删除数据等操作都是高风险目标。对于只读接口,CSRF 攻击的危害相对有限,但实际项目中很难完全区分,通常建议对所有状态变更请求启用防护。

Spring Boot 集成 Spring Security 的默认 CSRF 配置

在 Spring Boot 中引入 spring-boot-starter-security 依赖后,CSRF 防护默认开启。对于使用 Thymeleaf 等服务端模板渲染的项目,表单提交会自动包含一个名为 _csrf 的隐藏字段。当请求到达后端,CsrfFilter 会从请求参数中读取 _csrf 的值并与 Session 中保存的 Token 进行比较,不一致或缺失则返回 403 拒绝请求。这种机制对传统 MVC 项目非常友好,几乎不需要额外代码就能获得基础防护。

但对于需要自定义安全策略的场景,可以通过 SecurityFilterChain 配置来调整 CSRF 行为。下面是一个使用 Spring Security 5.7 及以上版本的 Java 配置示例,将 Token 存储方式改为 Cookie,方便前后端分离架构使用:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
            )
            .authorizeHttpRequests(auth -> auth
                .antMatchers("/login", "/error").permitAll()
                .anyRequest().authenticated()
            )
            .formLogin(form -> form.defaultSuccessUrl("/home"));
        return http.build();
    }
}

代码中使用了 CookieCsrfTokenRepository.withHttpOnlyFalse(),它会把 CSRF Token 写入名为 XSRF-TOKEN 的 Cookie 中,并将 HttpOnly 属性设为 false,这样前端 JavaScript 才能读取该 Cookie。如果 HttpOnly 为 true,浏览器 API 无法访问 Cookie,前后端分离场景下就无法使用此方案。值得注意的是,对于纯 API 服务且没有浏览器跨站请求风险时,有些开发者会选择使用 http.csrf().disable() 关闭防护,但这会带来安全隐患,不推荐在涉及用户身份的操作中直接禁用。

在传统表单页面中,Thymeleaf 会自动为每个表单插入 Token 字段,不需要开发者手动处理。但如果使用 JSP 或其他模板,可能需要手动输出 Token。随着 Spring Security 版本的演进,WebSecurityConfigurerAdapter 已被废弃,推荐使用组件式配置,上面的示例即为新写法,适用于 Spring Boot 2.7 及 3.x 版本。

前后端分离场景下 CSRF 防护实现

前后端分离架构下,后端不再渲染 HTML,前端通过 Axios、Fetch 等工具发送 AJAX 请求,无法依赖模板引擎自动注入 Token。此时需要前后端配合,常用的方案是:后端使用 CookieCsrfTokenRepository 将 Token 放入非 HttpOnly 的 Cookie,前端在发送请求前读取该 Cookie 的值,并添加到请求头 X-XSRF-TOKEN 中。Spring Security 的 CsrfFilter 会优先从请求头中读取该值进行校验。

后端配置可以沿用上面的 SecurityConfig,但需要注意 CORS 配置。由于前端可能运行在另一个域名或端口下,需要允许携带凭据的跨域请求。下面是包含 CORS 支持的完整后端配置:

@Configuration
@EnableWebSecurity
public class ApiSecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .cors().and()
            .csrf(csrf -> csrf
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
            )
            .authorizeHttpRequests(auth -> auth
                .antMatchers("/api/auth/**").permitAll()
                .anyRequest().authenticated()
            );
        return http.build();
    }
}

前端需要统一处理请求头。以 Axios 为例,可以添加一个请求拦截器,从 Cookie 中读取 XSRF-TOKEN 并设置到请求头。代码如下:

import axios from 'axios';

axios.defaults.withCredentials = true;

axios.interceptors.request.use(config => {
    const token = document.cookie.replace(/(?:(?:^|.*;\s*)XSRF-TOKEN\s*\=\s*([^;]*).*$)|^.*$/, '$1');
    if (token) {
        config.headers['X-XSRF-TOKEN'] = token;
    }
    return config;
});

上面的正则表达式用来解析 Cookie 字符串,提取 XSRF-TOKEN 的值。实际项目中也可以使用 js-cookie 等库简化读取过程。关键点是请求头名称必须为 X-XSRF-TOKEN,这是 Spring Security 默认支持的 Token 请求头,也可以通过自定义 CsrfTokenRepository 或 Filter 修改。当请求携带正确的 Token 后,CsrfFilter 会通过,否则返回 403。

还有一种做法是使用前后端约定的自定义请求头,比如 X-CSRF-TOKEN,但需要额外配置 Spring Security 的 CsrfFilter 参数。无论使用哪种请求头,核心思路都是确保请求中带上了与 Cookie 中 Token 匹配的值,并且攻击者无法从第三方域读取或设置该请求头,因为跨域请求默认无法携带自定义请求头,除非目标服务器通过 CORS 明确允许,而攻击者无法操纵目标服务器的 CORS 响应。这也解释了为什么 CSRF Token 方案在前后端分离下依然有效。

CSRF 防护测试与常见问题

验证 CSRF 防护是否生效,可以手动构造一个不带 Token 的 POST 请求,预期会返回 403 状态码。使用 curl 模拟时,先访问一次登录页面或者任意受保护页面,从响应头 Set-Cookie 中获取 XSRF-TOKEN,然后分别发送不带该 Token 和带正确 Token 的请求进行对比。如果带 Token 请求返回正常,不带 Token 请求被拒绝,说明防护配置成功。

在实际开发中,CSRF 防护常遇到一些诡异问题。例如前端已经配置了拦截器,但请求仍然被 403 拒绝,这通常是因为 CORS 预检请求(OPTIONS)没有正确通过。Spring Security 默认会拦截所有请求,OPTIONS 请求需要被 permitAll 放行,否则预检失败。此外,Cookie 作用域和域名配置不当也会导致 Token 无法传递,比如前端使用 localhost,后端使用 127.0.0.1,虽然指向同一台机器,但浏览器视为不同源,Cookie 不会共享。解决方法是统一使用域名或配置 Cookie 的 domain 属性。

另一个常见的误区是认为 SameSite Cookie 可以完全替代 CSRF Token。SameSite=Strict 或 Lax 确实能阻止大部分跨站请求携带 Cookie,从而缓解 CSRF 风险,但旧版浏览器不支持或支持不完整,而且某些场景下如从外部链接跳转,Lax 模式仍可能存在风险。因此,最佳实践是将 SameSite 属性与 CSRF Token 结合使用,形成纵深防御。Spring Security 的 CookieCsrfTokenRepository 默认不设置 SameSite,可以通过自定义 CookieSerializer 来添加该属性。

最后需要强调的是,CSRF 防护不能仅仅依赖后端配置,前端也应当对敏感操作增加确认步骤,例如输入验证码或二次密码。这样做即使 Token 被绕过,也能增加攻击成本。Spring Boot 与 Spring Security 的整合提供了灵活的 CSRF 防护能力,关键在于根据项目架构选择正确的实现方式,并充分测试跨域、Cookie 和 Token 的配合是否正常。

Spring BootCSRF防护Spring Security修改时间:2026-09-19 05:44:15

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0919/59117.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。