You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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正确携带:

  1. 服务器端修改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,
});
  1. 客户端去掉手动设置的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更安全。


额外优化建议
  1. 封装认证中间件,避免重复代码:
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,
  });
});
  1. 错误处理:避免直接throw err,返回对应的HTTP状态码和友好错误信息,防止服务器崩溃。

内容的提问来源于stack exchange,提问作者Sarthak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 03:39:52