为什么我的Anti-CSRF Token在跨域请求时失效了?
我在单页应用里用JWT做Anti-CSRF防护,每次登录后把token存在cookie并带上自定义请求头。但调用支付接口时后端返回403,明明请求头里带了正确的token值。
尝试过在Express后端设置了:
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'http://my-frontend.com');
res.header('Access-Control-Allow-Headers', 'X-CSRF-Token');
next();
});
前端用axios发送带token的POST请求,但Chrome开发者工具显示请求头里的X-CSRF-Token被浏览器拦截了。明明测试过同域请求没问题,跨域就失效
---
### 一、为什么你的 X-CSRF-Token 请求头被浏览器拦截了?
你遇到的现象是:**跨域请求时 X-CSRF-Token 被浏览器拦截,但同域没问题**。
这是因为:
1. 浏览器在跨域请求中会先发出一个
OPTIONS预检请求(preflight)。2. 如果后端没有正确响应这个
OPTIONS请求,浏览器就会**阻止后续的 POST 请求**。3. 特别是当请求头中包含非默认的字段(如
X-CSRF-Token)时,**必须显式允许这些 header**,否则浏览器不会放行。所以你看到的“请求头被拦截”,其实是浏览器出于安全机制,在预检阶段就阻止了该请求。
---
### 二、你的 Express 设置出了什么问题?
你设置了:
但这里有几个关键问题:
-
Access-Control-Allow-Headers应该在OPTIONS请求的响应中返回。- 你没有处理
OPTIONS请求本身。- 没有使用
cors这样的中间件,手动设置容易遗漏。---
### 三、正确解决方案
#### 1. 使用
cors中间件(推荐)安装:
使用方式(以 Express 为例):
> **说明**:
> -
origin要写前端地址,不能写成*,否则不能携带凭证。> -
credentials: true是关键,因为你要从 Cookie 里读 Token。> -
allowedHeaders要包含你用到的自定义 header,比如X-CSRF-Token。---
#### 2. 如果你一定要手动设置 CORS(不推荐)
你可以这样写中间件:
> **注意**:一定要处理
OPTIONS请求,否则浏览器不会继续发请求。---
### 四、前端 Axios 的设置也要注意
确保你设置了:
否则,即使后端允许携带 Cookie,浏览器也不会自动带上。
---
### 五、补充建议:Token 放在 Cookie + 请求头,是 Anti-CSRF 的标准做法
你现在的做法其实是对的:把 Token 放在 Cookie 和请求头中,后端做比对,这是防范 CSRF 的常见手段。
但跨域时需要特别注意以下几点:
| 注意点 | 是否满足 |
|--------|----------|
| 响应头中包含
Access-Control-Allow-Origin| ✅ || 响应头中包含
Access-Control-Allow-Credentials| ✅ || 响应头中允许
X-CSRF-Token这个自定义 header | ✅ || 正确响应浏览器的
OPTIONS请求 | ✅ || 前端设置了
withCredentials: true| ✅ |---
### 总结步骤
1. 使用
cors中间件或正确设置响应头2. 显式允许
X-CSRF-Tokenheader3. 处理
OPTIONS请求4. 设置
Access-Control-Allow-Credentials: true5. 前端设置
axios.defaults.withCredentials = true这样你的 Anti-CSRF Token 就能正常通过浏览器的跨域检查了。
如果还有问题,可以贴一下浏览器 Network 面板里
OPTIONS请求的响应内容,我可以帮你分析具体是哪一步出错了。关键点有两个:
1. 后端CORS配置要加
Access-Control-Allow-Credentials: true,否则带cookie的请求会被浏览器拦截2. 前端axios要设置
{withCredentials: true},否则跨域请求不会附带cookie但更重要的是,你现在的JWT+Anti-CSRF设计有问题。跨域场景下建议直接用双重提交Cookie模式:把CSRF token存到HTTP-only cookie里,前端通过隐藏表单字段或自定义请求头提交相同的token值,后端对比两者即可。
最后提醒一下,测试时记得清空浏览器缓存,不然容易被旧的CORS策略干扰。