1

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

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

我目前的实现是:

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');

有人设法将加密转移到猫鼬二传手吗?

问候,博多

4

1 回答 1

1

You DO have access to other properties from within the setter with the use of the this keyword. For example:

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

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

  return v;

});

However, setters are unfit for your use case. As you probably know, HMAC-SHA1 is super expensive and therefore will block unless performed asynchronously. Mongoose setters require the function to return a value and there is no way to route the result of crypto.pbkdf2()'s callback to the return value of the setter function. This is a limitation of asynchronous javascript and not Mongoose itself: you can't wrap an async call within a sync function, as this destroys the nature of the async chain.

Setters are most widely used for simple string manipulations and data sanitization.

Here is a demo for encryption using only instance methods:

// 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();
});
于 2012-11-05T21:27:51.073 回答