You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

重置密码功能$and查询异常:错误token仍可更新密码

解决重置密码时令牌错误仍能更新密码的问题

我看到你遇到的问题了:即使提供错误的重置令牌,用户密码还是会被更新,这是因为你的代码没有检查updateOne操作是否真的匹配到了目标文档。updateOne方法即使没有找到符合条件的文档,也会resolve Promise,只是返回结果里的matchedCount(或旧版本的n)会是0。另外,你使用bcrypt的回调嵌套方式不仅可读性差,还存在错误处理的漏洞(比如genSalt的错误没有被正确捕获)。

问题核心

你的代码里,执行users.updateOne(...)后直接返回成功响应,但没有验证是否真的有文档被修改。当令牌错误时,criteria无法匹配到任何用户,updateOne不会修改任何数据,但你的代码依然返回“密码重置成功”的提示。

修复方案

我们需要从三个方面调整代码:

  • updateOne的回调中检查是否有文档被匹配并修改
  • 改用bcrypt的Promise版本替代嵌套回调,提升代码可读性和错误处理能力
  • 完善错误分支,区分“令牌无效”和“服务器错误”两种场景

修复后的代码(推荐使用async/await)

// 确保这个逻辑包裹在async函数中(比如你的路由处理函数)
try {
  // 1. 验证用户输入(保留你已有的验证逻辑)

  // 2. 生成密码哈希
  const salt = await bcrypt.genSalt(saltRounds);
  const hashedPassword = await bcrypt.hash(form.newPassword, salt);

  // 3. 构建查询条件并执行更新
  const criteria = { 
    _id: oid, 
    reset_token: query.token 
  };
  const updateResult = await users.updateOne(
    criteria,
    { 
      $set: { password: hashedPassword }, 
      $unset: { reset_token: "" } 
    }
  );

  // 4. 检查是否匹配到有效文档
  if (updateResult.matchedCount === 0) {
    return res.status(400).json('Invalid reset token or user ID.');
  }

  // 5. 返回成功响应
  return res.status(201).json('Your password has been reset successfully!');
} catch (error) {
  console.error(error); // 记录错误日志便于排查问题
  return res.status(500).json('Sorry something is wrong. Please try again later.');
}

适配原回调风格的修复版本(不推荐,但兼容你的原有代码结构)

// setup find criteria
const criteria = { $and: [ {_id: oid}, {reset_token: query.token} ] };

// hashing new password
bcrypt.genSalt(saltRounds, (saltError, salt) => {
  if (saltError) {
    console.error(saltError);
    return res.status(500).json('Sorry something is wrong. Please try again later.');
  }

  bcrypt.hash(form.newPassword, salt, (hashError, hash) => {
    if (hashError) {
      console.error(hashError);
      return res.status(500).json('Sorry something is wrong. Please try again later.');
    }

    // update new password into DB and delete reset token
    users.updateOne(criteria, {$set:{password: hash}, $unset:{reset_token:""}} )
    .then(response => {
      // 关键:检查是否匹配到目标文档
      if (response.matchedCount === 0) {
        return res.status(400).json('Invalid reset token or user ID.');
      }
      return res.status(201).json('Your password has been reset successfully!');
    })
    .catch(dbError => {
      console.error(dbError);
      res.status(500).json('Sorry something is wrong. Please try again later.');
    });
  });
});

关键改进点

  • 验证更新有效性:通过updateResult.matchedCount判断查询条件是否匹配到用户,以此验证令牌和用户ID的合法性
  • 完善错误捕获:分别处理bcrypt生成盐、哈希密码以及数据库操作的错误,避免错误被遗漏
  • 代码可读性优化:用async/await替代嵌套回调,让逻辑流程更清晰直观

这样修改后,当用户提供错误的令牌时,updateOne会返回matchedCount: 0,此时我们会返回400错误提示,不会执行密码更新操作。

内容的提问来源于stack exchange,提问作者Irfan Gumelar

火山引擎 最新活动