密码重置后新密码无法登录:技术问题排查求助
问题描述
我正在实现用户忘记密码的重置功能,流程如下:
- 向
/user/forget-password发送账户邮箱,获取6位验证码 - 向
/user/reset-password/:userId发送重置验证码+新密码,收到成功提示:"Password updated successfully" - 使用邮箱和新密码登录
执行到第3步时,出现错误提示:"message":"Invalid password."
MongoDB集合中可见哈希密码已变更,但新密码仍无法登录。
相关代码
路由定义
router.post("/login", authController.signIn); router.post("/forget-password", authController.forgetPassword); router.post("/reset-password/:userId", authController.resetPasword);
控制器代码
module.exports.signIn = async (req, res) => { const { email, password } = req.body; const user = await UserModel.findOne({ email }); if (!user) { return res.status(400).json({ message: "User not found.", }); } const isValidPassword = await bcrypt.compare(password, user.password); if (!isValidPassword) { return res.status(400).json({ message: "Invalid password.", error: res.error }); } const token = jwt.sign({ id: user._id }, process.env.SECRET_KEY, { expiresIn: "2h", }); res.status(200).json({ message: "User logged in.", token, user: { id: user._id, email: user.email, firstName: user.firstName, lastName: user.lastName, }, }); }; module.exports.forgetPassword = async (req, res) => { const { email } = req.body; const resetCode = Math.floor(100000 + Math.random() * 900000).toString(); try { const user = await UserModel.findOneAndUpdate({ email }, { resetCode }); if (!user) return res.status(404).json({ message: "User not found" }); res.json({ message: "Reset code generated", userId: user._id }); } catch (error) { console.error(error); res.status(500).json({ message: "Server error" }); } }; module.exports.resetPasword = async (req, res) => { const { userId } = req.params; const { resetCode, newPassword } = req.body; try { const user = await UserModel.findById(userId); if (!user) return res.status(404).json({ message: "User not found" }); if (user.resetCode !== resetCode) return res.status(400).json({ message: "Invalid reset code" }); const salt = await bcrypt.genSalt(10); const newHashedPassword = await bcrypt.hash(newPassword, salt); user.password = newHashedPassword; user.resetCode = null; await user.save(); res.json({ message: "Password updated successfully" }); } catch (error) { console.error(error); res.status(500).json({ message: "Server error" }); } };
UserSchema定义
const UserSchema = new mongoose.Schema({ email: { type: String, required: [true, "Provide an email."], unique: true, match: [ /^([\w-\.]+@([\w-]+\.)+[\w-]{2,4})?$/, "Please, provide a valid email.", ], }, password: { type: String, required: [true, "Password is required."], }, firstname: { type: String, required: [true, "Firstname is required."], }, lastname: { type: String, required: [true, "Lastname is required."], }, resetCode: { type: String, default: null, } }); UserSchema.pre("save", async function (next) { const user = this; const salt = await bcrypt.genSalt(10); const hash = await bcrypt.hash(user.password, salt); user.password = hash; next(); }); UserSchema.methods.isValidPassword = async function (password) { const user = this; const compare = await bcrypt.compare(password, user.password); return compare; }; module.exports = mongoose.model("User", UserSchema);
问题原因与解决方案
核心问题
pre("save")钩子会在每次调用user.save()时重新哈希已经哈希过的密码。重置密码流程中:
- 你手动将
newPassword哈希为newHashedPassword并赋值给user.password - 调用
user.save()时,pre("save")钩子再次对这个已哈希的字符串进行二次哈希 - 登录时用原始新密码和二次哈希后的密码比对,自然不匹配
解决方案
修改pre("save")钩子,仅当密码字段被修改时才执行哈希操作,避免重复哈希:
UserSchema.pre("save", async function (next) { const user = this; // 只有password字段被修改时才重新哈希 if (!user.isModified("password")) return next(); const salt = await bcrypt.genSalt(10); const hash = await bcrypt.hash(user.password, salt); user.password = hash; next(); });
额外优化建议
- 重置密码时可直接使用
findByIdAndUpdate,减少数据库操作次数(可选):
module.exports.resetPasword = async (req, res) => { const { userId } = req.params; const { resetCode, newPassword } = req.body; try { const salt = await bcrypt.genSalt(10); const newHashedPassword = await bcrypt.hash(newPassword, salt); const user = await UserModel.findOneAndUpdate( { _id: userId, resetCode }, { password: newHashedPassword, resetCode: null }, { new: true } ); if (!user) return res.status(400).json({ message: "Invalid reset code or user not found" }); res.json({ message: "Password updated successfully" }); } catch (error) { console.error(error); res.status(500).json({ message: "Server error" }); } };
- 登录时使用模型定义的
isValidPassword方法,保持代码一致性:
// 在signIn控制器中替换原密码验证逻辑 const isValidPassword = await user.isValidPassword(password);
内容的提问来源于stack exchange,提问作者Johan
相关产品推荐
相关产品推荐

