NodeJS+Mongoose实现JWT登录时bcrypt.compare持续报错如何解决
问题原因分析
- 核心报错
Error: data and hash arguments required的直接原因是调用bcrypt.compare()时,传入的两个参数至少有一个为空或undefined:要么是请求体中未正确传递password字段,要么是查询返回的用户文档中不存在password字段。 - 控制台抛出的未捕获Promise拒绝警告,是因为
bcrypt.compare本身抛出的参数错误没有被提前捕获,会随着异步逻辑向上传递,虽然路由层有try/catch包裹,但提前校验参数可以避免这类不必要的报错。
修复步骤
- 先补充参数校验逻辑,避免空参数调用compare方法
修改静态登录方法代码如下:
UserSchema.statics.login = async function(userData){ // 先校验入参是否存在必填字段 const {email, password} = userData; if (!email || !password) { throw Error('邮箱和密码为必填项'); } const user = await this.findOne({email}); if (user) { // 校验返回的用户数据是否包含密码字段 if (!user.password) { throw Error('用户数据异常,请重新注册'); } const auth = await bcrypt.compare(password, user.password) if (auth) { return user; } throw Error('密码不匹配'); } throw Error('该邮箱未注册'); };
- 排查Schema配置问题
如果你在定义UserSchema时,为了安全给password字段设置了select: false属性,Mongoose查询时会默认隐藏该字段,导致返回的user文档没有password值,需要手动指定查询时返回密码:
将findOne语句修改为:
const user = await this.findOne({email}).select('+password');
- 确认数据正确性
- 检查接口请求是否正确传递了
password字段,字段名是否和代码中提取的一致,无拼写错误 - 直接查询MongoDB中的用户文档,确认对应邮箱的用户存在
password字段,且值为有效的bcrypt哈希串(标准bcrypt哈希为$2a$/$2b$开头的60位字符串)
- 优化路由层错误返回方便调试
修改登录路由的catch分支,把错误信息返回便于定位问题:
const post_login = async (req, res, next) => { const userData = req.body; try { const user = await User.login(userData); res.status(200).json({user: user._id}); } catch (error) { res.status(400).json({message: error.message}); } };
内容的提问来源于stack exchange,提问作者KrisnaRusdiono
相关产品推荐
相关产品推荐

