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

用户认证系统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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 06:47:39