const crypto = require('crypto') const mongoose = require('mongoose') mongoose.Promise = require('bluebird') var UserSchema = new mongoose.Schema({ name: String, loginId: { type: String, lowercase: true, required: true }, role: { type: String, default: 'user' }, password: { type: String, required: true }, provider: String, salt: String, token: String }) /** * Validations */ // Validate empty email UserSchema .path('loginId') .validate((loginId) => { return loginId.length }, '登陆名不能空') // Validate empty password UserSchema .path('password') .validate((password) => { return password.length }, '密码不能空') // Validate loginId is not taken UserSchema .path('loginId') .validate(function (value, respond) { return this.constructor.findOne({ loginId: value }).exec() .then(user => { if (user) { if (this.id === user.id) { return respond(true) } return respond(false) } return respond(true) }) .catch((err) => { throw err }) }, '该用户已存在') var validatePresenceOf = (value) => { return value && value.length } /** * Pre-save hook */ UserSchema .pre('save', function (next) { // Handle new/update passwords if (!this.isModified('password')) { return next() } if (!validatePresenceOf(this.password)) { return next(new Error('密码错误')) } // Make salt with a callback this.makeSalt((saltErr, salt) => { if (saltErr) { return next(saltErr) } this.salt = salt this.encryptPassword(this.password, (encryptErr, hashedPassword) => { if (encryptErr) { return next(encryptErr) } this.password = hashedPassword return next() }) }) }) /** * Methods */ UserSchema.methods = { /** * Authenticate - check if the passwords are the same * * @param {String} password * @param {Function} callback * @return {Boolean} * @api public */ authenticate(password, callback) { if (!callback) { return this.password === this.encryptPassword(password) } this.encryptPassword(password, (err, pwdGen) => { if (err) { return callback(err) } if (this.password === pwdGen) { return callback(null, true) } else { return callback(null, false) } }) }, /** * Make salt * * @param {Number} [byteSize] - Optional salt byte size, default to 16 * @param {Function} callback * @return {String} * @api public */ makeSalt(byteSize, callback) { var defaultByteSize = 16 if (typeof arguments[0] === 'function') { callback = arguments[0] byteSize = defaultByteSize; } else if (typeof arguments[1] === 'function') { callback = arguments[1] } else { throw new Error('却少回调方法') } if (!byteSize) { byteSize = defaultByteSize } return crypto.randomBytes(byteSize, (err, salt) => { if (err) { return callback(err) } else { return callback(null, salt.toString('base64')) } }) }, /** * Encrypt password * * @param {String} password * @param {Function} callback * @return {String} * @api public */ encryptPassword(password, callback) { if (!password || !this.salt) { if (!callback) { return null } else { return callback('却少密码或者加密内容') } } var defaultIterations = 10000 var defaultKeyLength = 64 var salt = new Buffer(this.salt, 'base64') if (!callback) { return crypto.pbkdf2Sync(password, salt, defaultIterations, defaultKeyLength) .toString('base64') } return crypto.pbkdf2(password, salt, defaultIterations, defaultKeyLength, (err, key) => { if (err) { return callback(err) } else { return callback(null, key.toString('base64')) } }) } } module.exports = mongoose.model('User', UserSchema)