Mongoose create () разрешен до завершения асинхронного промежуточного программного обеспечения - PullRequest
0 голосов
/ 04 мая 2018
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 голосов
/ 04 мая 2018

Короче говоря, вы забыли, что входите в функцию обратного вызова, которая имеет другую функциональную область, и вы все еще ссылаетесь на this, который в то время фактически не был экземпляром "модели".

Чтобы исправить это, сделайте копию 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()
  }


})()

Полный пример - Promise async / await

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
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...