用户认证系统OTP验证时password自动更新问题排查求助
问题排查与解决
问题根源
你的User Schema中pre("save")钩子的判断逻辑有误,导致每次调用user.save()更新字段时,都会重复加密密码。
原钩子代码的错误点:
if (!this.isModified) { next(); }
this.isModified是Mongoose文档的方法,不是属性。不带参数调用this.isModified()时,会判断整个文档是否有字段被修改——当你更新isVerified或isOtpExpired时,文档确实处于修改状态,所以这个条件永远不成立,钩子会执行密码加密逻辑,把已经加密过的密码再次哈希,最终导致密码失效。
修复方法
修改pre("save")钩子,仅在password字段被修改时才执行加密操作:
userSchema.pre("save", async function (next) { // 只有password字段被修改时,才重新加密 if (!this.isModified('password')) { return next(); } const salt = await bcrypt.genSalt(10); this.password = await bcrypt.hash(this.password, salt); next(); });
额外优化方案
如果只是更新isVerified、isOtpExpired这类非密码字段,建议直接使用findByIdAndUpdate操作数据库,这样可以跳过pre("save")钩子,提升效率:
比如在verifyUser函数中替换原有的保存逻辑:
// 替换 user.isVerified = true; await user.save(); const verifiedUser = await User.findByIdAndUpdate( req.user._id, { isVerified: true }, { new: true } // 返回更新后的用户文档 );
更新isOtpExpired时同理:
const otpExpiredUpdateUser = await User.findByIdAndUpdate( req.user._id, { isOtpExpired: true }, { new: true } );
这种方式减少了一次数据库查询(从"查询-修改-保存"变为直接更新),同时避免了不必要的密码加密操作。
内容的提问来源于stack exchange,提问作者Narayan Maity
相关产品推荐
相关产品推荐

