MERN JWT认证应用部署后客户端Cookie无法设置问题
我开发了一个基于MERN栈的JWT认证应用,服务器部署在https://api.somanyu.tech,客户端部署在https://finapp.somanyu.tech,二者均托管于Render并使用get.tech域名。
问题:客户端无法设置Cookie,浏览器中仅显示Cloudflare的__cf_bm、_cfuvid Cookie,预期的Token Cookie未出现。
后端设置Cookie代码(authController.js)
// Generate JWT Token. const token = jwt.sign({ _id: user._id }, process.env.JWT_SECRET, { expiresIn: "36h", }); console.log("GENERATED TOKEN\n", token); if (req.cookies[`${user._id}`]) { req.cookies[`${user._id}`] = ""; } // Set cookie. res.cookie(String(user._id), token, { httpOnly: true, path: '/*', expires: new Date(Date.now() + 100 * 30), maxAge: 1000 * 60 * 60 * 24 * 7, sameSite: 'none', domain: 'finapp.somanyu.tech', secure: true, }); return res.status(201).send({ success: "Signed in successfully." });
Token验证中间件代码(authMiddleware.js)
const verifyToken = async (req, res, next) => { // Retrieve cookies from header. const cookies = req.headers.cookie; console.log("🚀 ~ file: authMiddleware.js:7 ~ verifyToken ~ cookies:", cookies) if (cookies) { const cookie = cookies.split("=")[1]; const token = jwt.verify(cookie, process.env.JWT_SECRET, (error, user) => { if (error) return res.status(401).json({ error: "Invalid Token" }); req.user = user._id; // Id of the user who is logged in. next(); }); } else { return res.status(401).json({ error: "No Token" }); } }
本地环境一切正常,但部署后Cookie无法存储到浏览器。已确认Token可正常生成,但req.headers.cookie中仅存在Cloudflare的Cookie。这是我首次部署MERN应用,可能遗漏了关键步骤。
问题根源及修复方案
1. Cookie域名配置错误
服务器域名是api.somanyu.tech,设置Cookie时指定domain: 'finapp.somanyu.tech'属于跨域操作,浏览器会直接拒绝。正确做法是将domain设为父域名.somanyu.tech,这样两个子域名都能访问该Cookie;或者直接删除domain配置,让浏览器自动匹配当前服务器域名(需配合客户端跨域凭证配置)。
2. expires与maxAge冲突
代码中同时设置了expires和maxAge,且expires仅设置了30秒有效期,会导致Cookie立即过期。保留maxAge即可(建议和JWT的expiresIn保持一致,比如36小时),删除expires配置。
3. 客户端跨域请求未携带凭证
客户端发送登录请求时,必须开启凭证携带:
- Axios需配置
withCredentials: true - Fetch需设置
credentials: 'include'
否则浏览器不会保存跨域设置的Cookie。
4. 后端CORS配置缺失
服务器必须配置CORS允许携带凭证,示例代码:
const cors = require('cors'); app.use(cors({ origin: 'https://finapp.somanyu.tech', credentials: true, allowedHeaders: ['Content-Type'] }));
注意origin不能设为*,必须指定具体的客户端域名,否则浏览器会拒绝携带凭证的请求。
5. Cookie解析逻辑不严谨
中间件中cookies.split("=")[1]的写法会直接取第一个Cookie的值,无法精准获取目标Token。建议使用cookie-parser库简化Cookie读取:
- 安装依赖:
npm install cookie-parser - 在服务器中启用:
app.use(cookieParser()) - 中间件中修改为:
const verifyToken = async (req, res, next) => { // 假设登录时用user._id作为Cookie名称 const token = req.cookies[req.user?._id]; if (!token) return res.status(401).json({ error: "No Token" }); jwt.verify(token, process.env.JWT_SECRET, (error, user) => { if (error) return res.status(401).json({ error: "Invalid Token" }); req.user = user._id; next(); }); }
修复后的Cookie设置代码示例
res.cookie(String(user._id), token, { httpOnly: true, path: '/', maxAge: 1000 * 60 * 60 * 36, // 和JWT有效期一致(36小时) sameSite: 'none', domain: '.somanyu.tech', // 父域名共享Cookie secure: true, });
内容的提问来源于stack exchange,提问作者Protonic

