如何验证旧密码输入与存储密码一致?附代码求审核
解决密码修改功能的核心问题 & EJS代码审核
首先,你的核心问题出在旧密码的验证逻辑上——bcrypt的哈希机制自带随机盐值,所以同一个明文密码每次哈希的结果都不一样,你不能把输入的旧密码重新哈希后和数据库里的密码直接对比!下面一步步帮你修正所有问题:
一、修正后端密码验证逻辑
1. 替换错误的哈希对比方式
bcrypt专门提供了compare()方法来验证明文密码和哈希密码是否匹配,它会自动提取哈希密码里的盐值进行验证。把你代码里的这部分:
const oldHashedPassword = await bcrypt.hash(oldPassword, 10); if (user.password === oldHashedPassword && newPassword === confirmNewPassword) {
改成:
// 用bcrypt.compare验证明文旧密码和数据库哈希密码是否匹配 const isOldPasswordValid = await bcrypt.compare(oldPassword, user.password); if (isOldPasswordValid && newPassword === confirmNewPassword) {
2. 修复userId获取的错误
req.params.userId是同步值,不需要加await,直接改成:
const userId = req.params.userId;
3. 异步操作必须加await
user.save()是异步方法,不加await可能会导致页面跳转后数据库还没完成更新,加上:
await user.save();
4. 完善错误分支处理
现在你的catch只会捕获数据库异常,但旧密码错误、新密码不匹配这些业务错误不会进入catch,要在try里补充else逻辑:
else { // 区分不同错误类型,给用户更准确的提示 if (!isOldPasswordValid) { req.flash("error", "Old password is incorrect!"); } else if (newPassword !== confirmNewPassword) { req.flash("error", "New passwords do not match!"); } res.redirect("/settings/password"); }
修正后的完整后端代码
exports.postChangedPassword = async (req, res) => { const { oldPassword, newPassword, confirmNewPassword } = req.body; try { const userId = req.params.userId; // 去掉多余的await const user = await User.findById(userId); // 验证旧密码是否正确 const isOldPasswordValid = await bcrypt.compare(oldPassword, user.password); if (isOldPasswordValid && newPassword === confirmNewPassword) { const hashedPassword = await bcrypt.hash(newPassword, 10); user.password = hashedPassword; await user.save(); // 加上await确保保存完成 res.render("admin/settings/appliedSettings/changed-password", { pageTitle: "Succesfully Changed Password", path: "/settings/changed-password", user: user }); } else { // 处理业务错误 if (!isOldPasswordValid) { req.flash("error", "Old password is incorrect!"); } else { req.flash("error", "New passwords do not match!"); } res.redirect("/settings/password"); } } catch (error) { console.log(error); req.flash("error", "Something went wrong with the database!"); res.redirect("/settings/password"); } };
二、EJS代码审核与优化
你的EJS代码功能没问题,这里给几个体验优化建议:
- 关联label和input的id:现在label的
for属性都是password,但input没有对应的id,点击标签无法聚焦输入框,修正后:
<% if (errorMessage) { %> <div class="user-message-error"> <%= errorMessage %> </div> <% } %> <form class="change-password" action="/settings/changed-password/<%=user._id%>" method="POST"> <label for="oldPassword">Old Password</label> <input type="password" id="oldPassword" name="oldPassword" placeholder="Enter Your Old Password ..." required> <label for="newPassword">Your New Password</label> <input type="password" id="newPassword" name="newPassword" placeholder="Enter Your New Password ..." required> <label for="confirmNewPassword">Confirm Your New Password</label> <input type="password" id="confirmNewPassword" name="confirmNewPassword" placeholder="Confirm Your Password ..." required> <input type="hidden" name="_csrf" value="<%= csrfToken %>"> <button type="submit"> Submit Password </button> </form>
错误提示可以用
<%- %>输出:如果flash的error包含HTML(比如换行),用<%- %>可以正确解析,不过目前你的场景用<%= %>也没问题。可选:添加密码强度提示:如果需要提升安全性,可以在新密码输入框下方加提示,比如要求长度≥8位、包含字母和数字等,这属于额外体验优化。
补充说明
你的User模型里的pre-save钩子是正确的——它只会在密码字段被修改时重新哈希,避免了每次保存都重复哈希的问题,这部分不需要改动。
内容的提问来源于stack exchange,提问作者Tiger Abrodi
相关产品推荐
相关产品推荐

