Express.js+React JWT认证报错:JsonWebTokenError: jwt must be provided
问题描述
开发Express.js后端+React前端的Web应用时,采用JWT实现用户认证,服务器端验证token时出现如下错误:
JsonWebTokenError: jwt must be provided
服务器端配置:
- 使用
jsonwebtoken库生成和验证JWT /login路由处理登录,认证成功后发放JWT到cookie/post路由仅允许已认证用户创建帖子
相关服务器端核心代码:
// /post路由验证token部分 blobStream.on('finish', async () => { const [url] = await fileUpload.getSignedUrl({ action: 'read', expires: '03-01-2500', }); const { token } = req.cookies; // 从cookie读取token console.log('token',token); jwt.verify(token, secret, {}, async (err, info) => { if (err) throw err; const postDoc = await Post.create({ title, summary, content, cover: url, author: info.id, }); res.json(postDoc); }); });
客户端创建帖子代码:
// CreatePost组件fetch请求 const userToken = Cookies.get('token'); const headers = new Headers({ 'Authorization': `Bearer ${userToken}`, // token放在Authorization头 }); async function createNewPost(ev) { const data = new FormData(); // ... 构建FormData const response = await fetch('https://personal-website-on6a.onrender.com/post', { method: 'POST', body: data, headers: headers, credentials: 'include', withCredentials: true, }); }
问题原因
客户端将JWT放在Authorization请求头中发送,但服务器端代码却从req.cookies里读取token,两者传递方式不匹配,导致服务器无法获取到有效token,触发jwt must be provided错误。
修复方案
方案1:服务器端适配Authorization头读取token
修改所有需要认证的接口(/post、/put/post、/profile),从Authorization头提取Bearer token:
// 替换原token读取逻辑 const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { return res.status(401).json('未授权,请先登录'); } const token = authHeader.split(' ')[1]; // 提取Bearer后的token字符串 jwt.verify(token, secret, {}, async (err, info) => { if (err) { return res.status(403).json('无效的token'); } // 后续业务逻辑 });
方案2:客户端改用cookie传递token
如果想保留服务器从cookie读取token的逻辑,需确保跨域cookie正确携带:
- 服务器端修改cookie设置(适配跨域场景):
// /login路由中设置cookie时添加属性 res.cookie('token', token, { httpOnly: true, // 防止XSS攻击,前端无法通过JS读取 secure: true, // 生产环境开启,仅HTTPS下有效 sameSite: 'none', // 跨域场景必须设置 maxAge: 7 * 24 * 60 * 60 * 1000 // 7天有效期 }).json({ id: userDoc._id, username, });
- 客户端去掉手动设置的Authorization头,依赖
credentials: 'include'自动携带cookie:
// CreatePost组件中修改fetch请求 async function createNewPost(ev) { const data = new FormData(); // ... 构建FormData const response = await fetch('https://personal-website-on6a.onrender.com/post', { method: 'POST', body: data, credentials: 'include', // 自动携带cookie // 去掉自定义headers }); }
同时,客户端登录后无需用js-cookie存储token,服务器设置的httpOnly cookie更安全。
额外优化建议
- 封装认证中间件,避免重复代码:
const authenticateToken = (req, res, next) => { // 根据选择的方案,从header或cookie读取token const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { return res.status(401).json('未授权'); } const token = authHeader.split(' ')[1]; jwt.verify(token, secret, (err, info) => { if (err) return res.status(403).json('无效token'); req.user = info; // 将用户信息挂载到req对象 next(); }); }; // 使用中间件保护路由 app.post('/post', uploadMiddleware.single('file'), authenticateToken, async (req, res) => { // 直接使用req.user获取用户ID const postDoc = await Post.create({ title, summary, content, cover: url, author: req.user.id, }); });
- 错误处理:避免直接
throw err,返回对应的HTTP状态码和友好错误信息,防止服务器崩溃。
内容的提问来源于stack exchange,提问作者Sarthak
相关产品推荐
相关产品推荐

