Express权限验证中间件返回‘未授权’错误原因排查求助
权限中间件中ObjectId与字符串比较失败的原因及解决
我开发了一个权限保护中间件protect,通过JWT解析获取decoded.id并赋值给req.userId,同时查询数据库获取排除password字段的req.user。在verifyandAuth中间件中,控制台显示req.user._id与req.params.id完全相同,但直接用===比较会返回‘Not authorised’(未授权)错误;若将req.user._id替换为decoded.id进行比较,则中间件可正常通过,请问这一现象的原因是什么?
Protect 中间件代码
export const protect = async (req, res, next) => { let token; if ( req.headers.authorization && req.headers.authorization.startsWith("Bearer") ) try { { token = req.headers.authorization.split(" ")[1]; const decoded = jwt.verify(token, "kris"); req.userId = decoded.id; req.user = await User.findById(decoded.id).select("-password"); next(); } } catch (error) { res.status(400).json(error.message); } if (!token) { return res.status(400).json("Invalid Token"); } };
VerifyandAuth 中间件代码
export const verifyandAuth = (req, res, next) => { protect(req, res, () => { console.log(req.user._id, req.params.id); if (req.user._id === req.params.id || req.isAdmin) { next(); } else { res.status(400).json("Not authorised"); } }); };
问题原因
req.user._id是MongoDB返回的ObjectId对象,而req.params.id是从URL中提取的字符串类型。===属于严格相等比较,要求值和类型都完全一致,所以对象和字符串直接对比会返回false。- 控制台显示两者“相同”是因为控制台在打印时会自动将ObjectId对象转换为字符串形式展示,但实际内存中的数据类型并不一致。
decoded.id是从JWT payload中解析出的字符串(JWT存储的ID默认是字符串格式),和req.params.id类型一致,因此用===对比可以匹配成功。
解决方法
推荐以下几种可行的处理方式:
- 将ObjectId转为字符串后对比:
req.user._id.toString() === req.params.id - 使用MongoDB ObjectId自带的
equals方法:req.user._id.equals(req.params.id),该方法会自动处理类型差异,直接对比ID的实际值 - (可选)在JWT编码时存储ObjectId的字符串形式,确保解析后和URL参数类型统一(不过这一步不是必须的,前两种方法更直接)
内容的提问来源于stack exchange,提问作者Krishna Bharadwaj
相关产品推荐
相关产品推荐

