mongoose如何很好地处理密码编码?

时间:2012-11-05 17:45:48

标签: javascript node.js encryption mongoose

我想重构我的用户架构。这个决定的主要原因是我不想担心密码和盐的生成。所以我想将编码逻辑从预存储处理程序移动到setter。不幸的是,我无法从setter访问该对象的其他属性(如salt)。

因此默认盐不起作用并且用盐编码密码也不行。

我目前的实施是:

var userSchema = new mongoose.Schema({

    username: { 
        type: String, 
        index: { unique: true, sparse: true }, 
        required: true, lowercase: true, trim: true
    },

    email: {
        type: String,
        index: { unique: true, sparse: true }, 
        required: true, lowercase: true, trim: true
    },

    salt: {
        type: String,
        select: false
    },

    password: {
        type: String,
        select: false
    },

    plainPassword: {
        type: String,
        select: false
    }

});

// FIXME: password encoding only on change, not always
userSchema.pre('save', function(next) {
    // check if a plainPassword was set
    if (this.plainPassword !== '') {
        // generate salt
        crypto.randomBytes(64, function(err, buf) {
            if (err) return next(err);
            this.salt = buf.toString('base64');
            // encode password
            crypto.pbkdf2(this.plainPassword, this.salt, 25000, 512, function(err, encodedPassword) {
                if (err) return next(err);
                this.password = new Buffer(encodedPassword, 'binary').toString('base64');
                this.plainPassword = '';
            }.bind(this));
        }.bind(this));
    }

    next();
});

// statics
userSchema.methods.hasEqualPassword = function(plainPassword, cb) {
    crypto.pbkdf2(plainPassword, this.salt, 25000, 512, function(err, encodedPassword) {
        if (err) return next(err);
        encodedPassword = new Buffer(encodedPassword, 'binary').toString('base64');
        cb((this.password === encodedPassword));
    }.bind(this));
}

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

有人设法将加密移动到mongoose setter吗?

问候,bodo

1 个答案:

答案 0 :(得分:1)

您可以使用this关键字访问设置器中的其他属性。例如:

userSchema.path('pass').set(function(v) {

  console.log(this); // Returns model instance

  return v;

});

但是,setter不适合您的用例。您可能知道,HMAC-SHA1非常昂贵,因此除非异步执行,否则将阻止。 Mongoose setter要求函数返回一个值,并且无法将crypto.pbkdf2()的回调结果路由到setter函数的返回值。这是异步javascript的限制,而不是Mongoose本身:你不能在同步函数中包装异步调用,因为这会破坏异步链的本质。

Setter最常用于简单的字符串操作和数据清理。

以下是仅使用实例方法进行加密的演示:

// Model method
userSchema.methods.hashPassword = function(pass, callback) {
  // Generate salt (this should probably be async too)
  var salt = this.salt = crypto.createHash('md5').update(Math.random().toString()).digest('hex');
  // Salt and Hash password
  crypto.pbkdf2(pass, salt, 25000, 512, callback);
});

// Implementation
var user = new User({
  email: req.body.email
});
user.hashPassword(req.body.pass, function(err, hash){
  user.pass = hash; 
  user.save();
});