Mongoose使用findOneAndUpdate更新密码时未自动哈希加密问题
Mongoose使用findOneAndUpdate更新密码未哈希的解决方案
问题原因
你定义的pre("save")钩子仅会在调用文档实例的save()方法时触发,findOneAndUpdate属于直接操作数据库的查询方法,默认不会实例化文档对象,因此不会触发save钩子,导致明文密码直接入库。
解决方案
方案1:调整更新逻辑,复用现有save钩子(最推荐)
先查询到用户文档实例,修改密码属性后调用save()方法,直接复用你已经写好的密码哈希逻辑,无需修改Schema代码:
// 错误写法:不会触发save钩子 // await User.findOneAndUpdate({ _id: 目标用户ID }, { password: 明文新密码 }) // 正确写法 const user = await User.findOne({ _id: 目标用户ID }) if (!user) { // 自行处理用户不存在的逻辑 } user.password = 明文新密码 await user.save()
优点:逻辑最稳妥,所有涉及密码修改的逻辑都收敛在save钩子中,不会出现重复哈希的问题。
方案2:新增findOneAndUpdate前置钩子
如果必须使用findOneAndUpdate做更新,可以在Schema中新增对应钩子,单独处理更新场景下的密码哈希:
userSchema.pre("findOneAndUpdate", function (next) { const updateObj = this.getUpdate() // 兼容普通更新和$set两种更新写法 const rawPassword = updateObj.password ?? updateObj.$set?.password if (!rawPassword) return next() // 哈希密码后写回更新对象 const hashedPassword = bcrypt.hashSync(rawPassword, 8) if (updateObj.$set) { updateObj.$set.password = hashedPassword } else { updateObj.password = hashedPassword } next() })
注意:该钩子的上下文是查询对象而非文档实例,如果你的哈希逻辑依赖文档其他字段,该方案不适用。
方案3:更新前手动哈希密码
每次调用findOneAndUpdate更新密码前,手动对明文密码做哈希处理后再传入更新参数:
const hashedPassword = bcrypt.hashSync(明文新密码, 8) await User.findOneAndUpdate({ _id: 目标用户ID }, { password: hashedPassword })
缺点:每次更新密码都需要手动处理,容易遗漏导致明文入库,不推荐。
额外优化建议
你当前使用的bcrypt.hashSync、bcrypt.compareSync是同步方法,会阻塞Node.js事件循环,高并发场景下建议替换为异步的bcrypt.hash、bcrypt.compare方法,提升服务性能。
内容的提问来源于stack exchange,提问作者Srikanth Reddy
相关产品推荐
相关产品推荐

