用过mongoose的进来看看这是什么Nodejs问题

用过mongoose的进来看看这是什么Nodejs问题

首先我定义一个model:

var UserSchema = new Schema({
    username: { type: String, required: true, index: { unique: true } },
    password: { type: String, required: true }, 
    email: { type:String, required: true}
});
UserSchema.pre('save', function(next){
    var user = this;
    // only hash the password if it has been modified (or is new)
    if (!user.isModified('password')) return next(); 
    
    // generate a salt
    bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
        if (err) return next(err);

        // hash the password using our new salt
        bcrypt.hash(user.password, salt, function(err, hash) {
            console.log('user.password :',user.password);
            if (err) return next(err);

            // override the cleartext password with the hashed one
            user.password = hash;
            console.log('hash:',hash);
            console.log('user.password :',user.password);
            console.log('1')
            next();
        });
    }); 
    next();
});
module.exports = mongoose.model('User', UserSchema);

然后,我在路由里写:

app.post('/sign',function(req,res){
  console.log(req.body);
  var username = req.body.user,
   password = req.body.passwd,
   email = req.body.email;

  var newUser = new User({
   username: username,
   password: password,  
   email: email
  });
  
  newUser.save(function(err){
   if(err) throw err;  
   console.log('2')
   return res.redirect('/');
  });
 });

这样保存之后,在mongodb下查询密码还是明文。这个UserSchema.pre(‘save’,fn)是不是有问题?

console.log出来的是2,1。

我现在知道了是先save后再产生hash。请问我的写法是不是不对?我的理解是save之前先生成 hash,但是实现却不行。


4 回复

根据你提供的代码片段,问题在于 UserSchema.pre('save', fn)newUser.save() 的执行顺序。你提到 console.log 输出的结果是 2, 1,这表明 save 方法被调用了两次,并且 bcrypt.hash 还未完成时,save 方法就已经被执行了。

解决方案

我们需要确保在 save 方法调用之前,bcrypt.hash 已经完成。可以通过以下方式修改代码:

  1. 移除多余的 next() 调用:在 bcrypt.hash 回调中已经调用了 next(),所以在 bcrypt.genSalt 回调外面再调用 next() 是不必要的。
  2. 确保 save 方法只调用一次:确保 bcrypt.hash 完成后才调用 newUser.save()

以下是修改后的代码示例:

// 引入必要的模块
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');

// 定义用户模型
var UserSchema = new mongoose.Schema({
    username: { type: String, required: true, index: { unique: true } },
    password: { type: String, required: true }, 
    email: { type: String, required: true }
});

// 在保存前加密密码
UserSchema.pre('save', function(next){
    var user = this;
    
    // 只有在密码被修改时才进行加密
    if (!user.isModified('password')) return next();

    // 生成盐
    bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
        if (err) return next(err);

        // 加密密码
        bcrypt.hash(user.password, salt, function(err, hash) {
            if (err) return next(err);

            // 将明文密码替换为加密后的密码
            user.password = hash;
            next();
        });
    });
});

// 导出模型
module.exports = mongoose.model('User', UserSchema);

// 路由处理
app.post('/sign', function(req, res) {
    console.log(req.body);
    var username = req.body.user,
        password = req.body.passwd,
        email = req.body.email;

    var newUser = new User({
        username: username,
        password: password,  
        email: email
    });

    // 保存新用户
    newUser.save(function(err) {
        if (err) {
            console.error(err);
            return res.status(500).send('Error saving user.');
        }
        console.log('User saved successfully.');
        return res.redirect('/');
    });
});

解释

  1. 移除多余的 next():在 bcrypt.hash 回调内部已经调用了 next(),因此不需要在 bcrypt.genSalt 回调外部再次调用 next()
  2. 确保 save 只调用一次:确保在所有异步操作(如 bcrypt.hash)完成后才调用 newUser.save()

通过这些更改,可以确保在 save 操作之前,密码已经被正确地加密并替换为哈希值。


倒数第三行的next去掉试试

是多了一个next,我晕。不知道什么时候打多了一个

从你的描述来看,问题出在 UserSchema.pre('save', fn) 钩子函数中。next() 被调用了两次,一次是在bcrypt操作之前,另一次是在bcrypt操作之后。这会导致即使bcrypt尚未完成其操作,newUser.save() 也会被触发。

正确的做法是只在bcrypt操作完成之后调用 next(),确保密码已经被哈希处理后再保存用户信息。

示例修正后的代码

var UserSchema = new Schema({
    username: { type: String, required: true, index: { unique: true } },
    password: { type: String, required: true },
    email: { type: String, required: true }
});

UserSchema.pre('save', function(next) {
    var user = this;
    
    // only hash the password if it has been modified (or is new)
    if (!user.isModified('password')) return next();

    // generate a salt
    bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
        if (err) return next(err);

        // hash the password using our new salt
        bcrypt.hash(user.password, salt, function(err, hash) {
            if (err) return next(err);

            // override the cleartext password with the hashed one
            user.password = hash;
            next();
        });
    });

    // Comment out or remove the following line
    // next();
});

module.exports = mongoose.model('User', UserSchema);

解释

  1. 移除多余的 next():在bcrypt操作完成后才调用 next()
  2. 确保bcrypt操作完成:将所有相关的逻辑放在bcrypt的回调函数中,以确保只有当密码已经成功哈希后才继续执行。

通过这种方式,你可以确保密码在保存到数据库前已经被正确哈希。

回到顶部