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

如何验证旧密码输入与存储密码一致?附代码求审核

解决密码修改功能的核心问题 & 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代码功能没问题,这里给几个体验优化建议:

  1. 关联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>
  1. 错误提示可以用<%- %>输出:如果flash的error包含HTML(比如换行),用<%- %>可以正确解析,不过目前你的场景用<%= %>也没问题。

  2. 可选:添加密码强度提示:如果需要提升安全性,可以在新密码输入框下方加提示,比如要求长度≥8位、包含字母和数字等,这属于额外体验优化。

补充说明

你的User模型里的pre-save钩子是正确的——它只会在密码字段被修改时重新哈希,避免了每次保存都重复哈希的问题,这部分不需要改动。

内容的提问来源于stack exchange,提问作者Tiger Abrodi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 08:36:28