SpringBoot + Sa-Token 实现 CSRF 令牌校验(进阶篇)

前言

在上一篇中,我们通过 CORS 配置和 Origin/Referer 校验,搭建了基础的浏览器安全防护。但这些手段无法防御同源下的 CSRF 攻击,也无法完全避免工具伪造请求。本文将结合 Sa-Token 自带的 SaCsrfUtil,实现随机、单次有效的 CSRF 令牌校验,完成安全防护的最后一环。


一、CSRF 令牌的核心原理

CSRF 令牌的防御逻辑非常简单:

  1. 用户登录后,服务器生成一个随机、一次性有效的 CSRF 令牌,返回给前端。
  2. 前端在发起敏感请求(POST/PUT/DELETE)时,必须携带这个令牌。
  3. 服务器校验令牌的有效性,校验通过后立即作废,确保令牌无法复用。

二、Sa-Token CSRF 令牌的使用

1. 生成并返回 CSRF 令牌

在用户登录或页面加载时,生成令牌并返回给前端:

java

运行

kotlin 复制代码
import cn.dev33.satoken.csrf.SaCsrfUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/auth")
public class AuthController {

    // 生成一次性 CSRF 令牌
    @GetMapping("/csrf-token")
    public String getCsrfToken() {
        return SaCsrfUtil.createToken();
    }
}

2. 在 Sa-Token 拦截器中校验令牌

在之前的拦截器中,添加敏感请求的 CSRF 令牌校验逻辑:

java

运行

less 复制代码
@Slf4j
@Configuration
public class SaTokenSecurityConfig {

    @Value("${spring.profiles.active:dev}")
    private String activeProfile;

    @Value("#{'${system.cors.origin_url:}'.split(',')}")
    private List<String> allowedOrigins;

    @Bean
    public SaInterceptor saInterceptor() {
        return new SaInterceptor(handler -> {
            // 1. 基础登录校验
            StpUtil.checkLogin();

            // 2. 开发环境 Origin/Referer 校验
            if (!Arrays.asList("test", "stg", "prod").contains(activeProfile)) {
                // 校验逻辑同上一篇,此处省略
            }

            // 3. 敏感请求 CSRF 令牌校验
            String method = handler.getRequest().getMethod();
            if (Arrays.asList("POST", "PUT", "DELETE").contains(method)) {
                SaCsrfUtil.checkToken();
            }
        });
    }
}

三、前端如何携带 CSRF 令牌?

前端在发起敏感请求时,需要将令牌放入请求头中,Sa-Token 默认会从 X-CSRF-Token 头中读取令牌:

javascript

运行

javascript 复制代码
// 1. 页面加载时获取令牌
let csrfToken = "";
fetch("/auth/csrf-token")
  .then(res => res.text())
  .then(token => {
    csrfToken = token;
  });

// 2. 发起敏感请求时携带令牌
fetch("/api/user/update", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${localStorage.getItem('token')}`,
    "X-CSRF-Token": csrfToken,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({ name: "新名字" })
});

四、完整防护闭环:四种手段协同工作

现在我们已经实现了全部四种浏览器安全防护手段,它们各司其职,形成了完整的防护闭环:

表格

防护手段 作用 防御场景
SameSite/HttpOnly/Secure Cookie 让浏览器不跨站携带 Cookie,且防止 JS 读取 跨站 CSRF、XSS 窃取 Cookie
CORS 跨域配置 让浏览器主动拒绝非白名单跨域请求 浏览器发起的跨域请求
Origin/Referer 校验 服务器端校验请求来源,拒绝非法域名 工具伪造请求、绕过 CORS 的请求
CSRF 令牌校验 敏感请求必须携带一次性有效令牌 同源 CSRF 攻击、工具重放请求

五、生产环境部署注意事项

  1. 强制 HTTPS :所有环境必须使用 HTTPS,否则 secure: true 配置会导致 Cookie 无法携带。
  2. 令牌过期时间:可根据业务场景调整 CSRF 令牌的有效期,避免影响用户体验。
  3. 日志监控:对 Origin/Referer 校验失败、CSRF 令牌校验失败的请求,添加日志监控,及时发现异常攻击行为。
  4. 避免通配符配置 :生产环境严禁使用 * 作为跨域允许的 Origin,必须配置明确的白名单。

结语

浏览器安全防护不是靠某一种手段就能实现的,而是多种手段协同工作的结果。通过 Sa-Token 框架,我们可以快速实现 CORS、Origin/Referer 校验、Cookie 安全属性和 CSRF 令牌校验,为项目搭建起坚固的安全防线。希望本文能帮你解决跨域与 CSRF 防护的痛点,写出更安全的前后端分离项目代码。

相关推荐
Full Stack Developme3 小时前
AspectJ 详解
java·后端
元宝骑士3 小时前
SpringBoot + Sa-Token 实现浏览器级 CSRF 防御(基础篇)
spring boot·安全
武子康3 小时前
Java-20 深入浅出 MyBatis - 手写ORM框架1 从原始 JDBC 暴露的 6 大问题开始
java·后端
雪隐3 小时前
AI股票小助手06-Backtrader 量化回测
人工智能·后端
设计师小聂!3 小时前
Java异常处理
java·开发语言·后端·编辑器·idea
ihuyigui3 小时前
国际商超零售短信接口
大数据·前端·后端·架构·零售
SimonKing3 小时前
实用,DynamicTP进阶之数据采集与告警
java·后端·程序员
用户298698530143 小时前
Java 进阶:基于模板生成 Word 文档的实践思路
java·后端
枕星而眠3 小时前
C++ 面向对象核心机制深度解析:多态性、虚函数、虚继承与 final 类
运维·开发语言·c++·后端