为什么我的Anti-CSRF Token在跨域请求时失效了?

百里玲玲 阅读 79

我在单页应用里用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被浏览器拦截了。明明测试过同域请求没问题,跨域就失效

我来解答 赞 8 收藏
二维码
手机扫码查看
2 条解答
司空倩云
这其实是很多新手在处理跨域和 CSRF 防护时踩坑的地方。你设置的逻辑没错,但顺序和方式有点问题,特别是在 Express 的中间件配置上。下面我一步步解释原因,并给出正确的做法。

---

### 一、为什么你的 X-CSRF-Token 请求头被浏览器拦截了?

你遇到的现象是:**跨域请求时 X-CSRF-Token 被浏览器拦截,但同域没问题**。

这是因为:

1. 浏览器在跨域请求中会先发出一个 OPTIONS 预检请求(preflight)。
2. 如果后端没有正确响应这个 OPTIONS 请求,浏览器就会**阻止后续的 POST 请求**。
3. 特别是当请求头中包含非默认的字段(如 X-CSRF-Token)时,**必须显式允许这些 header**,否则浏览器不会放行。

所以你看到的“请求头被拦截”,其实是浏览器出于安全机制,在预检阶段就阻止了该请求。

---

### 二、你的 Express 设置出了什么问题?

你设置了:

res.header('Access-Control-Allow-Origin', 'http://my-frontend.com');
res.header('Access-Control-Allow-Headers', 'X-CSRF-Token');


但这里有几个关键问题:

- Access-Control-Allow-Headers 应该在 OPTIONS 请求的响应中返回。
- 你没有处理 OPTIONS 请求本身。
- 没有使用 cors 这样的中间件,手动设置容易遗漏。

---

### 三、正确解决方案

#### 1. 使用 cors 中间件(推荐)

安装:

npm install cors


使用方式(以 Express 为例):

const express = require('express');
const cors = require('cors');
const app = express();

const corsOptions = {
origin: 'http://my-frontend.com', // 前端地址
credentials: true, // 允许携带凭证(如 Cookie)
allowedHeaders: ['Content-Type', 'X-CSRF-Token'], // 允许的请求头
};

app.use(cors(corsOptions));


> **说明**:
> - origin 要写前端地址,不能写成 *,否则不能携带凭证。
> - credentials: true 是关键,因为你要从 Cookie 里读 Token。
> - allowedHeaders 要包含你用到的自定义 header,比如 X-CSRF-Token

---

#### 2. 如果你一定要手动设置 CORS(不推荐)

你可以这样写中间件:

app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'http://my-frontend.com');
res.header('Access-Control-Allow-Credentials', 'true');
res.header('Access-Control-Allow-Headers', 'Content-Type, X-CSRF-Token');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');

if (req.method === 'OPTIONS') {
return res.sendStatus(200); // 浏览器预检通过
}

next();
});


> **注意**:一定要处理 OPTIONS 请求,否则浏览器不会继续发请求。

---

### 四、前端 Axios 的设置也要注意

确保你设置了:

axios.defaults.withCredentials = true;


否则,即使后端允许携带 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-Token header
3. 处理 OPTIONS 请求
4. 设置 Access-Control-Allow-Credentials: true
5. 前端设置 axios.defaults.withCredentials = true

这样你的 Anti-CSRF Token 就能正常通过浏览器的跨域检查了。

如果还有问题,可以贴一下浏览器 Network 面板里 OPTIONS 请求的响应内容,我可以帮你分析具体是哪一步出错了。
点赞 7
2026-02-03 12:04
UX-记彤
UX-记彤 Lv1
兄弟,你这问题我之前也踩过坑,别走弯路了。跨域时浏览器对自定义请求头有严格限制,不是简单设置CORS就能解决的。

关键点有两个:

1. 后端CORS配置要加 Access-Control-Allow-Credentials: true,否则带cookie的请求会被浏览器拦截
2. 前端axios要设置 {withCredentials: true},否则跨域请求不会附带cookie

但更重要的是,你现在的JWT+Anti-CSRF设计有问题。跨域场景下建议直接用双重提交Cookie模式:把CSRF token存到HTTP-only cookie里,前端通过隐藏表单字段或自定义请求头提交相同的token值,后端对比两者即可。

// 后端CORS配置示例
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'http://my-frontend.com');
res.header('Access-Control-Allow-Credentials', 'true');
res.header('Access-Control-Allow-Headers', 'X-CSRF-Token');
next();
});

// 前端axios配置
const instance = axios.create({
withCredentials: true
});


最后提醒一下,测试时记得清空浏览器缓存,不然容易被旧的CORS策略干扰。
点赞 15
2026-02-01 09:05