更新用户接口报错Cannot read properties of undefined (reading 'id')
问题报错说明
开发用户信息更新接口时,请求PUT /:id接口触发报错:Cannot read properties of undefined (reading 'id'),请求始终无法通过verifyTokenAndAuthorization中间件校验。
控制台日志顺序:
- 首先打印日志
req recived for login - 随后抛出上述错误,报错定位在
verifyToken.js第20行,未打印req went past verification日志
涉及的问题代码:
用户更新接口:
router.put("/:id", verifyTokenAndAuthorization, async (req, res) => { if (req.user.password) { req.body.password = CryptoJS.AES.encrypt( req.body.password, process.env.PASS_SEC ).toString(); } try { const updatedUser = await User.findByIdAndUpdate( req.user.id, { $set: req.body, }, { new: true } ); res.status(200).json(updatedUser); } catch (err) { res.status(500).json(err); } });
权限校验中间件:
const verifyTokenAndAuthorization = (req, res, next) => { verifyToken(req, res, () => { console.log("req recived for login"); if (req.user.id === req.params.id || req.user.isAdmin) { console.log("req went past verification"); next(); } else { res.status(403).json("You are not allowed to access!"); } }); };
根因定位
报错触发点是权限校验中间件里的req.user.id读取逻辑:执行到这行时req.user的值为undefined,读取undefined的id属性直接抛错。
结合执行链路可确定核心问题出在前置的verifyToken中间件:
verifyToken在token解析失败、请求未携带合法token的场景下,没有提前返回401错误,仍然调用了传入的回调函数,导致逻辑走到了id判断的分支verifyToken没有在JWT解析成功后,将解析出的用户信息正确挂载到req.user属性上,导致回调中访问不到req.user
另外现有代码还存在两个隐藏逻辑bug:
- 更新接口中判断是否需要加密密码的条件写为
req.user.password,req.user是JWT解析出的信息,不会存储用户明文密码,该判断永远不会生效,用户提交的新密码不会被加密 - 更新用户时查询条件写为
req.user.id,和路由定义的/:id参数、权限校验逻辑不匹配,管理员更新其他用户信息时会错误修改当前登录账号的信息
修复方案
1. 修复verifyToken中间件逻辑
确保verifyToken仅在JWT解析成功、正确挂载req.user后才调用next()执行后续逻辑,异常场景提前返回错误,参考实现:
const jwt = require('jsonwebtoken'); const verifyToken = (req, res, next) => { const authHeader = req.headers.authorization; if (!authHeader) { return res.status(401).json("未携带认证凭证"); } const token = authHeader.split(" ")[1]; jwt.verify(token, process.env.JWT_SEC, (err, payload) => { if (err) { return res.status(401).json("token无效或已过期"); } // 解析成功后将用户信息挂载到req.user req.user = payload; next(); }) }
2. 修复用户更新接口逻辑
调整密码加密判断条件、用户更新查询条件,和权限逻辑对齐:
router.put("/:id", verifyTokenAndAuthorization, async (req, res) => { // 加密用户提交的新密码,不是req.user上的密码 if (req.body.password) { req.body.password = CryptoJS.AES.encrypt( req.body.password, process.env.PASS_SEC ).toString(); } try { // 用路由参数中的id作为更新查询条件,匹配权限校验逻辑 const updatedUser = await User.findByIdAndUpdate( req.params.id, { $set: req.body, }, { new: true } ); res.status(200).json(updatedUser); } catch (err) { res.status(500).json(err); } });
3. 校验请求格式
前端发起请求时,需要在请求头中正确携带token,格式为:Authorization: Bearer <登录接口返回的有效token>
内容的提问来源于stack exchange,提问作者Ajmal Rahman
相关产品推荐
相关产品推荐

