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

为何用户注册时密码未被bcrypt哈希化?

问题:用户注册时密码未被哈希,数据库中仍为明文

我通过以下代码可以成功添加新用户,但查看数据库时发现密码仍为明文,显然存在问题。在let newUser = new User部分,密码从请求体传入,若无错误,bcrypt应哈希密码并将newUser.password设为哈希值,对吗?

//ADD USER Submit POST Route
router.post('/register', [
  check('name').isLength({min:1}).trim().withMessage('Name required'),
  check('email').isLength({min:1}).trim().withMessage('Email required'),
  check('email').isEmail().trim().withMessage('Email is not valid'),
  check('password').isLength({min:1}).withMessage('Password required'),
  check('password').custom((value,{req, loc, path}) => {
    if (value !== req.body.password2) {
      // throw error if passwords do not match
      throw new Error("Passwords do not match");
    } else {
      return value;
    }
  })
], (req,res,next)=>{
  let newUser = new User({
    name:req.body.name,
    email:req.body.email,
    username:req.body.username,
    password: req.body.password
  });
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    console.log(errors);
    res.render('register', {
      newUser:newUser,
      errors: errors.mapped()
    });
  } else{
    bcrypt.genSalt(10, function(err, salt) {
      bcrypt.hash(newUser.password, salt, function(err, hash) {
        if(err) {
          console.log(err);
        }
        newUser.name = req.body.name;
        newUser.email = req.body.email;
        newUser.username = req.body.username;
        newUser.password = hash;
      })
    })
    newUser.save(err=>{
      if(err)throw err;
      req.flash('success','You are now registered and can log in');
      res.redirect('/users/login');
    });
  }
});

问题分析

你踩了异步编程的经典坑:bcrypt.genSalt和bcrypt.hash都是异步函数,它们的回调逻辑会在主线程任务完成后才执行。但你在调用这两个异步函数之后,立刻执行了newUser.save()——这时候哈希密码的操作还没跑完,newUser.password仍然是请求体里的明文,所以数据库里存的自然就是未加密的密码了。

解决办法

给你两种修复方案,选一种顺手的用就行:

方案1:把保存操作放到哈希回调内部

确保只有当密码哈希完成后,才执行保存用户的逻辑:

// 前面的验证代码不变
} else{
  bcrypt.genSalt(10, function(err, salt) {
    if(err) {
      console.log(err);
      return next(err); // 别忘了处理错误,避免程序崩溃
    }
    bcrypt.hash(newUser.password, salt, function(err, hash) {
      if(err) {
        console.log(err);
        return next(err);
      }
      // 哈希完成后再更新密码并保存
      newUser.password = hash;
      // 这里没必要重复赋值name/email/username,创建newUser时已经设置过了
      newUser.save(err=>{
        if(err) {
          console.log(err);
          return next(err);
        }
        req.flash('success','You are now registered and can log in');
        res.redirect('/users/login');
      });
    })
  })
}

方案2:用async/await简化异步流程

嵌套回调容易搞出“回调地狱”,用ES6的async/await能让代码更简洁易读:

// 把路由处理函数改成async函数
router.post('/register', [
  // 验证规则保持不变
], async (req,res,next)=>{
  let newUser = new User({
    name:req.body.name,
    email:req.body.email,
    username:req.body.username,
    password: req.body.password
  });
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    console.log(errors);
    return res.render('register', {
      newUser:newUser,
      errors: errors.mapped()
    });
  } else{
    try {
      // 等待盐生成和密码哈希完成
      const salt = await bcrypt.genSalt(10);
      const hash = await bcrypt.hash(newUser.password, salt);
      newUser.password = hash;
      // 等待用户保存操作完成
      await newUser.save();
      req.flash('success','You are now registered and can log in');
      res.redirect('/users/login');
    } catch(err) {
      console.log(err);
      next(err); // 把错误传给全局错误处理中间件
    }
  }
});

额外小提醒

  • 你在哈希回调里重复赋值name/email/username完全没必要,创建newUser实例时已经给这些字段赋过值了,只需要更新password就行。
  • 所有异步操作都要做好错误处理,别让错误静默消失或者直接抛出导致程序崩溃。

内容的提问来源于stack exchange,提问作者ty.loves.code

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:55:23