Подтвердить что ты не робот

Хеширование паролей Mongoose

Я ищу хороший способ сохранить учетную запись в MongoDB с помощью mongoose.

Моя проблема: пароль хешируется асинхронно. Сеттер не будет работать здесь, потому что он работает только синхронно.

Я подумал о двух способах:

  • Создайте экземпляр модели и сохраните ее в обратном вызове хэш-функции.

  • Создание предварительного перехвата на 'save'

Есть ли хорошее решение этой проблемы?

4b9b3361

Ответ 1

В блоге mongodb есть отличная публикация о том, как реализовать аутентификацию пользователя.

http://blog.mongodb.org/post/32866457221/password-authentication-with-mongoose-part-1

Ниже скопировано следующее из ссылки:

Модель пользователя

var mongoose = require('mongoose'),
    Schema = mongoose.Schema,
    bcrypt = require('bcrypt'),
    SALT_WORK_FACTOR = 10;

var UserSchema = new Schema({
    username: { type: String, required: true, index: { unique: true } },
    password: { 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();
        });
    });
});

UserSchema.methods.comparePassword = function(candidatePassword, cb) {
    bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
        if (err) return cb(err);
        cb(null, isMatch);
    });
};

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

Использование

var mongoose = require(mongoose),
    User = require('./user-model');

var connStr = 'mongodb://localhost:27017/mongoose-bcrypt-test';
mongoose.connect(connStr, function(err) {
    if (err) throw err;
    console.log('Successfully connected to MongoDB');
});

// create a user a new user
var testUser = new User({
    username: 'jmar777',
    password: 'Password123';
});

// save user to database
testUser.save(function(err) {
    if (err) throw err;
});

// fetch user and test password verification
User.findOne({ username: 'jmar777' }, function(err, user) {
    if (err) throw err;

    // test a matching password
    user.comparePassword('Password123', function(err, isMatch) {
        if (err) throw err;
        console.log('Password123:', isMatch); // -> Password123: true
    });

    // test a failing password
    user.comparePassword('123Password', function(err, isMatch) {
        if (err) throw err;
        console.log('123Password:', isMatch); // -> 123Password: false
    });
});

Ответ 2

Официальное решение Mongoose требует, чтобы модель была сохранена до использования метода verifyPass, что может вызвать путаницу. Будет ли следующая работа для вас? (Я использую scrypt вместо bcrypt).

userSchema.virtual('pass').set(function(password) {
    this._password = password;
});

userSchema.pre('save', function(next) {
    if (this._password === undefined)
        return next();

    var pwBuf = new Buffer(this._password);
    var params = scrypt.params(0.1);
    scrypt.hash(pwBuf, params, function(err, hash) {
        if (err)
            return next(err);
        this.pwHash = hash;
        next();
    });
});

userSchema.methods.verifyPass = function(password, cb) {
    if (this._password !== undefined)
        return cb(null, this._password === password);

    var pwBuf = new Buffer(password);
    scrypt.verify(this.pwHash, pwBuf, function(err, isMatch) {
        return cb(null, !err && isMatch);
    });
};

Ответ 3

Я думаю, было бы лучше использовать крючок, после некоторого исследования, которое я нашел

http://mongoosejs.com/docs/middleware.html

где говорится:

Случаи использования:

асинхронные значения по умолчанию

Я предпочитаю это решение, потому что я могу инкапсулировать это и гарантировать, что учетную запись можно сохранить только с паролем.

Ответ 4

Другой способ сделать это с помощью методов виртуальных машин и экземпляров:

/**
 * Virtuals
 */
schema.virtual('clean_password')
    .set(function(clean_password) {
        this._password = clean_password;
        this.password = this.encryptPassword(clean_password);
    })
    .get(function() {
        return this._password;
    });

schema.methods = {

    /**
     * Authenticate - check if the passwords are the same
     *
     * @param {String} plainText
     * @return {Boolean}
     * @api public
     */
    authenticate: function(plainPassword) {
        return bcrypt.compareSync(plainPassword, this.password);
    },

    /**
     * Encrypt password
     *
     * @param {String} password
     * @return {String}
     * @api public
     */
    encryptPassword: function(password) {
        if (!password)
            return '';

        return bcrypt.hashSync(password, 10);
    }
};

Просто сохраните свою модель, как виртуальная, выполнит свою работу.

var user = {
    username: "admin",
    clean_password: "qwerty"
}

User.create(user, function(err,doc){});