Next.js电商站客户端JWT Cookie获取为undefined问题排查
我使用Next.js和Mongoose搭建了电商网站,通过Cookie存储JWT令牌。在客户端控制台的Application面板中,可以看到名为OursiteJWT的Cookie及对应令牌值:
但多次测试发现,获取令牌时返回undefined,控制台输出“pas de token”,密钥是正确的。
pages/orders.js 代码
import { useEffect, useState } from "react"; import axios from "axios"; import jwt from "jsonwebtoken"; import cookie from "cookie"; function Orders() { const [orders, setOrders] = useState([]); useEffect(() => { async function fetchOrders() { try { // 从API获取密钥 const secretRes = await axios.get("/api/secret"); const secret = secretRes.data.secret; console.log(secret); // 从Cookie获取JWT令牌 const cookies = cookie.parse(document.cookie); const token = cookies.OursiteJWT; console.log(token); // 若无令牌则返回 if (!token) { console.log("pas de token") return; } // 验证JWT令牌 const decoded = jwt.verify(token, secret); const userId = decoded._id; // 从API获取用户订单 const ordersRes = await axios.get(`/api/orders?user_id=${userId}`); const orders = ordersRes.data; setOrders(orders); } catch (error) { console.error(error); } } fetchOrders(); }, []); return ( <div> {orders.map((order) => ( <div key={order._id}> <h2>Commande {order._id}</h2> <p>Utilisateur: {order.name}</p> <p>Adresse: {order.address}</p> <p>Ville: {order.city}</p> <p>Produits:</p> <ul> {order.products.map((product) => ( <li key={product._id}>{product.name}</li> ))} </ul> </div> ))} </div> ); } export default Orders;
存储令牌的 api/login.js 代码
import {initMongoose} from "../../lib/mongoose"; import User from '../../models/User'; import { sign } from "jsonwebtoken"; import { serialize } from "cookie"; initMongoose() const secret = process.env.SECRET; export default async function handler(req,res){ const {email,password}=req.body; const user = await User.findOne({email,password}); if(!user){ res.status(401).json({message: "Impossible de trouver un utilisateur"}); return; } else{ const token = sign( { exp: Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 30, // 30 days email: email, _id: user._id, }, secret ); const serialised = serialize("OursiteJWT", token, { httpOnly: true, secure: process.env.NODE_ENV !== "development", sameSite: "strict", maxAge: 60 * 60 * 24 * 30, path: "/", }); res.setHeader("Set-Cookie", serialised); res.status(200).json({message :"Success!"}); } }
问题原因及解决方法
- 核心原因:设置Cookie时开启了
httpOnly: true,该属性会禁止客户端JavaScript访问Cookie,因此document.cookie无法读取到OursiteJWT。
解决思路(二选一):
调整Cookie属性(不推荐生产环境):
如果必须在客户端读取令牌,将login.js中的httpOnly改为false:const serialised = serialize("OursiteJWT", token, { httpOnly: false, // 修改此处 secure: process.env.NODE_ENV !== "development", sameSite: "strict", maxAge: 60 * 60 * 24 * 30, path: "/", });注意:关闭
httpOnly会增加XSS攻击风险,生产环境不建议使用。安全最佳实践(推荐):
不在客户端处理JWT验证和订单请求,改为服务器端完成:- 修改
/api/orders接口,让它自动从请求Cookie中读取OursiteJWT,验证后返回用户订单 - 客户端直接调用
/api/orders,无需手动获取令牌:// pages/orders.js 中fetchOrders修改为: async function fetchOrders() { try { const ordersRes = await axios.get("/api/orders"); setOrders(ordersRes.data); } catch (error) { console.error(error); } } - 服务器端
/api/orders示例:import { initMongoose } from "../../lib/mongoose"; import Order from "../../models/Order"; import jwt from "jsonwebtoken"; import cookie from "cookie"; initMongoose(); const secret = process.env.SECRET; export default async function handler(req, res) { if (req.method !== "GET") { return res.status(405).json({ message: "方法不允许" }); } // 从请求头Cookie中读取令牌 const cookies = cookie.parse(req.headers.cookie || ""); const token = cookies.OursiteJWT; if (!token) { return res.status(401).json({ message: "未授权" }); } try { const decoded = jwt.verify(token, secret); const orders = await Order.find({ user_id: decoded._id }); res.status(200).json(orders); } catch (error) { res.status(401).json({ message: "令牌无效" }); } }
这种方式保留了
httpOnly的安全特性,避免了客户端处理敏感令牌的风险。- 修改
内容的提问来源于stack exchange,提问作者Rip Rip
相关产品推荐
相关产品推荐

