无法将httpOnly Cookie中的JWT刷新令牌发送回服务器问题排查
我正在实现JWT访问令牌/刷新令牌机制,将refresh token存入httpOnly Cookie中并希望将其回传至服务器。该httpOnly Cookie在响应的Set-Cookie头中存在,但在Chrome开发者工具的Application标签中不可见(不确定是否正常,有说法称httpOnly Cookie因安全原因不显示)。
现有代码
CORS配置
const corsOptions = { origin: "http://localhost:5173", credentials: true, //access-control-allow-credentials:true optionSuccessStatus: 200, exposedHeaders: [ "x-auth-token", "Access-Control-Allow-Origin", "Access-Control-Allow-Credentials", ], }; app.use(cookieParser()); app.use(cors(corsOptions));
向客户端发送Cookie的接口代码
res .cookie("refreshToken", refreshToken, { httpOnly: true, origin: "http://localhost:3000", secure: true, maxAge: 7 * 24 * 60 * 60 * 1000, }) .header("x-auth-token", accessToken) .send("User validated, token accesible from headers");
接收令牌的中间件(console.log打印undefined)
module.exports = function authorizeRefreshToken(req, res, next) { try { const refreshToken = req.cookie; console.log("Refresh Token from client : ", refreshToken); if (!refreshToken) return res.status(403).send("No token inside cookie."); const decoded = jwt.verify( refreshToken, config.get("jwtPrivateKey"), (err) => { if (err) { if (err.name === "TokenExpiredError") { return res.status(403).send("Unauthorized"); } else { console.log(err); return res.status(400).send("An error with JWT occurred!"); } } } ); req._id = decoded._id; req.refreshToken = refreshToken; next(); } catch (error) { console.log("Error with the token", error); } };
前端API调用代码
const getAccessToken = async () => { try { const response = await axios.get(END_POINT, { withCredentials: true, }); const token = response.headers["x-auth-token"]; setAccessToken(token); console.log("Access Token in useEffect : ", token); } catch (error) { console.error("AXIOS ERROR", error); } };
相关截图
- 服务器响应头中的Set-Cookie:

- Chrome开发者工具Application标签:

问题分析与解决方法
1. 中间件获取Cookie的错误
你在中间件里写的req.cookie是错误的,cookieParser中间件解析后,客户端发送的Cookie会存放在req.cookies(复数)对象中。正确的获取方式应该是:
const refreshToken = req.cookies.refreshToken;
2. Cookie配置错误(导致Cookie未被客户端存储)
(1)origin不是res.cookie的有效选项
res.cookie的配置里没有origin这个参数,应该替换为sameSite和domain:
domain:设置为'localhost',允许跨端口(5173和3000)共享Cookie;sameSite:结合CORS的credentials: true,建议设置为'none'(需要配合secure: true),或者开发环境用'lax'。
(2)secure: true在HTTP环境下无效
如果你的本地开发环境是HTTP(而非HTTPS),secure: true会导致浏览器拒绝存储Cookie,因为该选项要求Cookie只能通过HTTPS传输。开发环境可以暂时将其设为false,生产环境再改回true。
修正后的Cookie配置:
res .cookie("refreshToken", refreshToken, { httpOnly: true, domain: "localhost", sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax', secure: process.env.NODE_ENV === 'production', maxAge: 7 * 24 * 60 * 60 * 1000, }) .header("x-auth-token", accessToken) .send("User validated, token accesible from headers");
3. Chrome Application标签看不到httpOnly Cookie的原因
httpOnly Cookie在Chrome的Application标签中是可以看到的,只是会标记为HttpOnly,无法通过JS的document.cookie访问。你看不到说明Cookie根本没被浏览器存储,本质是上面的Cookie配置错误导致的,修正配置后就能在Application标签中看到该Cookie。
4. JWT验证的回调问题
你在jwt.verify中使用了回调函数,此时decoded会返回undefined(因为回调模式下不会返回解码结果),应该改用同步模式或者正确处理回调:
同步模式写法:
try { const decoded = jwt.verify(refreshToken, config.get("jwtPrivateKey")); req._id = decoded._id; req.refreshToken = refreshToken; next(); } catch (err) { if (err.name === "TokenExpiredError") { return res.status(403).send("Unauthorized"); } else { console.log(err); return res.status(400).send("An error with JWT occurred!"); } }
或者保持回调模式,但要在回调内部处理后续逻辑:
jwt.verify(refreshToken, config.get("jwtPrivateKey"), (err, decoded) => { if (err) { if (err.name === "TokenExpiredError") { return res.status(403).send("Unauthorized"); } else { console.log(err); return res.status(400).send("An error with JWT occurred!"); } } req._id = decoded._id; req.refreshToken = refreshToken; next(); });
内容的提问来源于stack exchange,提问作者Νίκος Ανδρεάδης

