在异步中间件完成之前,Mongoose create()已解析

时间:2018-05-04 12:27:34

标签: node.js express mongoose

var mongoose = require('mongoose');

var bcrypt = require('bcrypt-nodejs');

var UserSchema = new mongoose.Schema({
  email: {
    type: string,
    unique: true,
    required: true,
    trim: true
  },
  password: {
    type: string,
    required: true
  },
    authtokens: {
        type: [{ type: mongoose.Schema.Types.ObjectId, ref: 'AuthToken' }]
    }
});

//hashing a password before saving it to the database
UserSchema.pre('save', function (next) {
    if (this.isNew) {
        bcrypt.gensalt(10, function(err, salt) {
            if (err) return next(err);
            bcrypt.hash(this.password, salt, null, function (err, hash){
                if (err) return next(err);
                this.password = hash;
                console.log('user.password ', this.password);
                next();
            });
        });
    } else next();
});

我从控制器那里打电话:

'use strict';

var mongoose = require('mongoose'),
    User = mongoose.model('User'),
    AuthToken = mongoose.model('AuthToken');

exports.createUser = function(req, res, next) {
    if (req.body.email && req.body.password && req.body.passwordConf) {
        var userData = {
            email: req.body.email,
            password: req.body.password,
            passwordConf: req.body.passwordConf
        };
        //use schema.create to insert data into the db
        User.create(userData, function (err, user) {
            console.log('user created ', user.password);
            if (err) {
                return next(err);
            } else {
                return res.redirect('/profile');
            }
        });
    } else {
        var err = new Error("Missing parameters");
        err.status = 400;
        next(err);
    }
};

当使用电子邮件createUser,密码user@email.com调用password时,我会收到输出:

user.password  $2a$10$wO.6TPUm5b1j6lvHdCi/JOTeEXHWhYernWU.ZzA3hfYhyWoOeugcq
user created  password

另外,直接查看数据库,我看到此用户使用纯文本密码 - > password

为什么用户在数据库中有明文密码。我该如何存储哈希?

1 个答案:

答案 0 :(得分:1)

简而言之,你忘记了你进入了一个具有不同功能范围的回调,而你仍然指的是this,那时候实际上并不是"模型&#34 ;实例

要解决此问题,请在执行任何操作之前获取this的副本,例如使用回调启动其他功能:

UserSchema.pre('save', function(next) {
  var user = this;              // keep a copy
  if (this.isNew) {
    bcrypt.genSalt(10, function(err,salt) {
      if (err) next(err);
      bcrypt.hash(user.password, salt, null, function(err, hash) {
        if (err) next(err);
        user.password = hash;
        next();
      });
    });
  }
});

当然,另一种方法是使事物现代化,并使用Promise async/await结果。 bcrypt库实际上是"核心"而不是叉子开箱即用:

UserSchema.pre('save', async function() {
  if (this.isNew) {
    let salt = await bcrypt.genSalt(10);
    let hash = await bcrypt.hash(this.password, salt);
    this.password = hash;
  }
});

除了现代方法通常是更清晰的代码之外,您还不需要更改this的范围,因为我们不会"潜入"到另一个函数调用。在同一范围内,所有内容都会发生变化,当然在继续之前等待异步调用。

完整示例 - 回调

const { Schema } = mongoose = require('mongoose');
const bcrypt = require('bcrypt-nodejs');

const uri = 'mongodb://localhost/crypto';

var userSchema = new Schema({
  email: String,
  password: String
});

userSchema.pre('save', function(next) {
  var user = this;          // keep a copy
  if (this.isNew) {
    bcrypt.genSalt(10, function(err,salt) {
      if (err) next(err);
      bcrypt.hash(user.password, salt, null, function(err, hash) {
        if (err) next(err);
        user.password = hash;
        next();
      });
    });
  }
});

const log = data => console.log(JSON.stringify(data, undefined, 2));

const User = mongoose.model('User', userSchema);

(async function() {

  try {

    const conn = await mongoose.connect(uri);

    await Promise.all(Object.entries(conn.models).map(([k,m]) => m.remove()));

    await User.create({ email: 'ted@example.com', password: 'password' });

    let result = await User.findOne();
    log(result);

  } catch(e) {
    console.error(e)
  } finally {
    process.exit()
  }


})()

完整示例 - 承诺异步/等待

const { Schema } = mongoose = require('mongoose');
const bcrypt = require('bcrypt');

const uri = 'mongodb://localhost/crypto';

var userSchema = new Schema({
  email: String,
  password: String
});

userSchema.pre('save', async function() {
  if (this.isNew) {
    let salt = await bcrypt.genSalt(10);
    let hash = await bcrypt.hash(this.password, salt);
    this.password = hash;
  }
});

const log = data => console.log(JSON.stringify(data, undefined, 2));

const User = mongoose.model('User', userSchema);

(async function() {

  try {

    const conn = await mongoose.connect(uri);

    await Promise.all(Object.entries(conn.models).map(([k,m]) => m.remove()));

    await User.create({ email: 'ted@example.com', password: 'password' });

    let result = await User.findOne();
    log(result);

  } catch(e) {
    console.error(e)
  } finally {
    process.exit()
  }


})()

两者都显示正确加密的密码,因为我们实际上在模型实例中设置了值:

{
  "_id": "5aec65f4853eed12050db4d9",
  "email": "ted@example.com",
  "password": "$2b$10$qAovc0m0VtmtpLg7CRZmcOXPDNi.2WbPjSFkfxSUqh8Pu5lyN4p7G",
  "__v": 0
}