前言
在上一篇中,我们通过 CORS 配置和 Origin/Referer 校验,搭建了基础的浏览器安全防护。但这些手段无法防御同源下的 CSRF 攻击,也无法完全避免工具伪造请求。本文将结合 Sa-Token 自带的 SaCsrfUtil,实现随机、单次有效的 CSRF 令牌校验,完成安全防护的最后一环。
一、CSRF 令牌的核心原理
CSRF 令牌的防御逻辑非常简单:
- 用户登录后,服务器生成一个随机、一次性有效的 CSRF 令牌,返回给前端。
- 前端在发起敏感请求(POST/PUT/DELETE)时,必须携带这个令牌。
- 服务器校验令牌的有效性,校验通过后立即作废,确保令牌无法复用。
二、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 攻击、工具重放请求 |
五、生产环境部署注意事项
- 强制 HTTPS :所有环境必须使用 HTTPS,否则
secure: true配置会导致 Cookie 无法携带。 - 令牌过期时间:可根据业务场景调整 CSRF 令牌的有效期,避免影响用户体验。
- 日志监控:对 Origin/Referer 校验失败、CSRF 令牌校验失败的请求,添加日志监控,及时发现异常攻击行为。
- 避免通配符配置 :生产环境严禁使用
*作为跨域允许的 Origin,必须配置明确的白名单。
结语
浏览器安全防护不是靠某一种手段就能实现的,而是多种手段协同工作的结果。通过 Sa-Token 框架,我们可以快速实现 CORS、Origin/Referer 校验、Cookie 安全属性和 CSRF 令牌校验,为项目搭建起坚固的安全防线。希望本文能帮你解决跨域与 CSRF 防护的痛点,写出更安全的前后端分离项目代码。