MERN应用JWT存Cookie后请求始终返回401问题排查
核心问题定位
问题不在代理或基础CORS配置,核心原因是JWT的提取逻辑不匹配:
登录后将JWT存在了名为token的Cookie中,但Passport的JWT策略配置里,仅设置从Authorization头的Bearer Token提取(ExtractJwt.fromAuthHeaderAsBearerToken()),完全未读取Cookie中的token,导致认证失败返回401。
修复步骤
1. 修改JWT提取策略,支持从Cookie读取token
修改认证中间件的opts配置,新增从Cookie提取token的逻辑,同时保留Bearer Token方式做兼容:
var opts = {}; // 替换原有的提取方式,同时支持Cookie和Bearer Token opts.jwtFromRequest = ExtractJwt.fromExtractors([ ExtractJwt.fromAuthHeaderAsBearerToken(), (req) => { let token = null; if (req && req.cookies) { token = req.cookies.token; } return token; } ]); opts.secretOrKey = config.secretKey;
2. 完善Cookie安全配置(防XSS/CSRF)
既然是为了规避XSS风险,建议将Cookie的httpOnly设为true,禁止前端JS读取Cookie,同时补充其他安全参数:
res.cookie('token', token, { httpOnly: true, // 关键:禁止前端JS访问Cookie,彻底防XSS窃取 secure: process.env.NODE_ENV === 'production', // 生产环境开启,仅在HTTPS下传输 sameSite: 'strict', // 防止CSRF攻击,根据业务场景可选lax maxAge: 3600 * 1000 // 和token过期时间保持一致(3600秒) });
3. 修正CORS配置,确保凭证传递
在CORS选项委托中,需明确开启credentials: true,否则浏览器不会自动发送Cookie:
var corsOptionsDelegate = (req, callback) => { var corsOptions; if(whitelist.indexOf(req.header('Origin')) !== -1) { // 同时允许源和凭证传递 corsOptions = { origin: true, credentials: true }; } else { corsOptions = { origin: false }; } callback(null, corsOptions); }
同时,目标路由的GET方法统一使用corsWithOptions,保持与OPTIONS请求的配置一致:
adminRouter.route('/match') .options(cors.corsWithOptions, (req, res) => { res.sendStatus(200); }) .get(cors.corsWithOptions, authenticate.verifyUser, authenticate.verifyAdmin, (req,res,next) => { // 路由逻辑 })
4. 优化代理配置(若使用React代理)
如果package.json已配置代理"proxy": "http://192.168.1.18:3005",客户端请求应使用相对路径,避免直接跨域请求:
await axios.get('/admin/match',{ withCredentials: true })
验证方法
- 登录成功后,打开浏览器开发者工具
Application标签,确认Cookie列表中存在token字段 - 发起请求时,查看
Network标签的请求头,确认Cookie字段已携带token=xxx - 服务器端可在
verifyUser中间件前添加日志,打印req.cookies.token,验证是否能正确获取到token
内容的提问来源于stack exchange,提问作者user14398375
相关产品推荐
相关产品推荐

