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

Node.js+MongoDB Atlas邮箱验证问题:登录失败与文档异常

问题解决方案

1. Bcrypt密码比对始终返回false的问题

原因

你的pre("save")钩子会在每次保存用户文档时重新哈希user_password字段——包括验证码验证通过后调用user.save()、更新用户信息时的user.save()。这会导致原本已经哈希过的密码被二次哈希,登录时用原始密码和二次哈希后的密码比对必然失败。

解决方案

修改Schema中的pre("save")钩子,仅当密码字段是新创建或被修改时才执行哈希操作:

user_schema.pre("save", async function (next) {
  try {
    // 仅在密码字段被修改(或新创建)时执行哈希
    if (!this.isModified("user_password")) {
      return next();
    }
    const hash = await bcrypt.hash(this.user_password, 15);
    this.user_password = hash;
    next();
  } catch (error) {
    next(error);
  }
});

2. 15分钟未验证自动删除、验证后误删的问题

原因

当前Schema中createdAt字段设置了固定的expires: "60m"TTL索引,无论用户是否验证通过,文档都会在创建后60分钟被删除。验证时更新createdAt只是延长了过期时间,无法彻底取消过期规则。

解决方案

改用条件TTL索引,仅对未验证用户(isEmailValidated: false)应用15分钟过期规则,验证通过后自动终止删除逻辑:

  1. 修改Schema,移除createdAt的直接expires配置,添加条件索引:
const user_schema = new Schema({
  // ... 其他字段保持不变
  isEmailValidated: {
    type: Boolean,
    default: false, 
  },
  createdAt: {
    type: Date,
    default: Date.now,
  },
  // ... 其他字段保持不变
});

// 添加条件TTL索引:未验证用户15分钟后自动删除
user_schema.index(
  { createdAt: 1 },
  { expireAfterSeconds: 15 * 60, partialFilterExpression: { isEmailValidated: false } }
);
  1. 调整验证码验证逻辑,无需更新createdAt,只需标记验证通过即可:
async (req,res)=>{
  const {email, verificationCode} = req.body  
  console.log(email, verificationCode)
  try {
    const user = await Model.findOne({ user_email: email, validationCode: verificationCode });
    if (user) {
      // 标记验证通过,条件TTL索引会自动停止对该文档的删除
      user.isEmailValidated = true;
      // 可选:验证通过后清除验证码,避免重复使用
      user.validationCode = undefined;
      await user.save();
      console.log('Email validation successful.');
      return res.status(201).json({
        message: `The Validation is done Successfully `,
      });
    } else {
      console.error('Email validation failed: Invalid code or email.');
      return res.status(400).json({message: 'Invalid verification code or email'});
    }
  } catch (error) {
    console.error('Error verifying email:', error);
    return res.status(500).json({message: 'Verification failed', error: error.message});
  }
},

额外优化建议

  • 登录时确保使用正确的bcrypt比对逻辑:
// 登录示例代码
async function login(req, res) {
  const { user_email, user_password } = req.body;
  try {
    const user = await Model.findOne({ user_email });
    if (!user || !user.isEmailValidated) {
      return res.status(401).json({message: 'User not found or not verified'});
    }
    const isMatch = await bcrypt.compare(user_password, user.user_password);
    if (isMatch) {
      // 登录成功逻辑
      return res.status(200).json({message: 'Login successful'});
    } else {
      return res.status(401).json({message: 'Invalid password'});
    }
  } catch (error) {
    res.status(500).json({message: 'Login failed', error: error.message});
  }
}
  • 可给验证码单独添加过期时间,避免用户使用过期验证码。

内容的提问来源于stack exchange,提问作者Hani Danial

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 11:37:49