Mongoose密码哈希

时间:2013-01-29 16:55:22

标签: node.js mongodb mongoose

我正在寻找一种使用mongoose将帐户保存到MongoDB的好方法。

我的问题是:密码是异步散列的。 setter不会在这里工作,因为它只能同步工作。

我想到了两种方式:

  • 创建模型的实例并将其保存在。的回调中 哈希函数。

  • 在'save'

  • 上创建预挂钩

这个问题有什么好的解决方案吗?

11 个答案:

答案 0 :(得分:105)

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

答案 1 :(得分:3)

我认为这是用户Mongoose和bcrypt的好方法!

用户模型

/**
 * Module dependences
*/

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

// define User Schema
const UserSchema = new Schema({
    username: {
        type: String,
        unique: true,
        index: {
            unique: true
        }
    },
    hashed_password: {
        type: String,
        default: ''
    }
});

// Virtuals
UserSchema
    .virtual('password')
    // set methods
    .set(function (password) {
        this._password = password;
    });

UserSchema.pre("save", function (next) {
    // store reference
    const user = this;
    if (user._password === undefined) {
        return next();
    }
    bcrypt.genSalt(SALT_WORK_FACTOR, function (err, salt) {
        if (err) console.log(err);
        // hash the password using our new salt
        bcrypt.hash(user._password, salt, function (err, hash) {
            if (err) console.log(err);
            user.hashed_password = hash;
            next();
        });
    });
});

/**
 * Methods
*/
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);

用法

signup: (req, res) => {
    let newUser = new User({
        username: req.body.username,
        password: req.body.password
    });
    // save user
    newUser.save((err, user) => {
        if (err) throw err;
        res.json(user);
    });
}

结果

Result

答案 2 :(得分: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 :(得分:2)

对于愿意使用ES6 +语法的用户可以使用此-

const bcrypt = require('bcryptjs');
const mongoose = require('mongoose');
const { isEmail } = require('validator');

const { Schema } = mongoose;
const SALT_WORK_FACTOR = 10;

const schema = new Schema({
  email: {
    type: String,
    required: true,
    validate: [isEmail, 'invalid email'],
    createIndexes: { unique: true },
  },
  password: { type: String, required: true },
});

schema.pre('save', async function save(next) {
  if (!this.isModified('password')) return next();
  try {
    const salt = await bcrypt.genSalt(SALT_WORK_FACTOR);
    this.password = await bcrypt.hash(this.password, salt);
    return next();
  } catch (err) {
    return next(err);
  }
});

schema.methods.validatePassword = async function validatePassword(data) {
  return bcrypt.compare(data, this.password);
};

const Model = mongoose.model('User', schema);

module.exports = Model;

答案 4 :(得分:2)

TL; DR-打字稿解决方案

我正在寻找相同的解决方案但使用打字稿时到达了这里。因此,对于任何对上述问题的TS解决方案感兴趣的人,这里都是我最终使用的示例。

导入和&竞争:

import mongoose, { Document, Schema, HookNextFunction } from 'mongoose';
import bcrypt from 'bcryptjs';

const HASH_ROUNDS = 10;

简单的用户界面和架构定义:

export interface IUser extends Document {
    name: string;
    email: string;
    password: string;
    validatePassword(password: string): boolean;
}

const userSchema = new Schema({
    name: { type: String, required: true },
    email: { type: String, required: true, unique: true },
    password: { type: String, required: true },
});

用户模式预保存挂钩实现

userSchema.pre('save', async function (next: HookNextFunction) {
    // here we need to retype 'this' because by default it is 
    // of type Document from which the 'IUser' interface is inheriting 
    // but the Document does not know about our password property
    const thisObj = this as IUser;

    if (!this.isModified('password')) {
        return next();
    }

    try {
        const salt = await bcrypt.genSalt(HASH_ROUNDS);
        thisObj.password = await bcrypt.hash(thisObj.password, salt);
        return next();
    } catch (e) {
        return next(e);
    }
});

密码验证方法

userSchema.methods.validatePassword = async function (pass: string) {
    return bcrypt.compare(pass, this.password);
};

和默认导出

export default mongoose.model<IUser>('User', userSchema);

注意:不要忘记安装类型包(@types/mongoose@types/bcryptjs

答案 5 :(得分:0)

我想在找到一些研究后,最好使用钩子

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

它说:

用例:

异步默认值

我更喜欢这个解决方案,因为我可以封装它,并确保只能使用密码保存帐户。

答案 6 :(得分:0)

使用虚拟和实例方法执行此操作的另一种方法:

/**
 * 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){});

答案 7 :(得分:0)

const bcrypt = require('bcrypt');

const saltRounds = 5;
const salt = bcrypt.genSaltSync(saltRounds);

module.exports = (password) => {
  return bcrypt.hashSync(password, salt);
}

const mongoose = require('mongoose')
const Schema = mongoose.Schema
const hashPassword = require('../helpers/hashPassword')

const userSchema = new Schema({
  name: String,
  email: {
    type: String,
    match: [/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, `Please fill valid email address`],
    validate: {
      validator: function() {
        return new Promise((res, rej) =>{
          User.findOne({email: this.email, _id: {$ne: this._id}})
              .then(data => {
                  if(data) {
                      res(false)
                  } else {
                      res(true)
                  }
              })
              .catch(err => {
                  res(false)
              })
        })
      }, message: 'Email Already Taken'
    }
  },
  password: {
    type: String,
    required: [true, 'Password required']
  }
});

userSchema.pre('save', function (next) {
  if (this.password) {
      this.password = hashPassword(this.password)
  }
  next()
})

const User = mongoose.model('User', userSchema)

module.exports = User

答案 8 :(得分:0)

const mongoose = require('mongoose');
var bcrypt = require('bcrypt-nodejs');
SALT_WORK_FACTOR = 10;

const userDataModal = mongoose.Schema({
    username: {
        type: String,
        required : true,
        unique:true
    },
    password: {
        type: String,
        required : true
    }

});

userDataModal.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, null, function(err, hash) {
            if (err) return next(err);

            // override the cleartext password with the hashed one
            user.password = hash;
            next();
        });
    });
});

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


// Users.index({ emaiId: "emaiId", fname : "fname", lname: "lname" });

const userDatamodal = module.exports = mongoose.model("usertemplates" , userDataModal)



//inserting document
     userDataModel.findOne({ username: reqData.username }).then(doc => {
            console.log(doc)
            if (doc == null) {
                let userDataMode = new userDataModel(reqData);
               // userDataMode.password = userDataMode.generateHash(reqData.password);
                userDataMode.save({new:true}).then(data=>{
                          let obj={
                              success:true,
                              message: "New user registered successfully",
                              data:data
                          }
                            resolve(obj)
                }).catch(err=>{
                                reject(err)
                })

            }
            else {
                resolve({
                    success: true,
                    docExists: true,
                    message: "already user registered",
                    data: doc
                }
                )
            }

        }).catch(err => {
            console.log(err)
            reject(err)
        })

//retriving and checking
      // test a matching password
                user.comparePassword(requestData.password, function(err, isMatch) {
                    if (err){ 

                        reject({
                            'status': 'Error',
                            'data': err
                        });

                        throw err;
                    } else  {
                        if(isMatch){

                            resolve({   
                                'status': true,
                                'data': user,
                                'loginStatus' : "successfully Login"
                            });

                            console.log('Password123:', isMatch); // -&gt; Password123: true

                        }

答案 9 :(得分:0)

我使用了 .find({email}) 而不是 .findOne({email})。

确保使用 .findOne(...) 获取用户。

示例:

const user = await <user>.findOne({ email });

答案 10 :(得分:-1)

我的推荐 我认为需要输入PWD哈希输入才能进行功能比较

控制器

        let newUser = new User(req.body);
        User.findOne({ username: req.body.username }, function(err, User) {
            if (err) throw err;
            // test a matching password
            newUser.comparePassword(req.body.pwd,User.pwd, function(err, isMatch) {
                if (err) throw err;
                console.log(req.body.pwd, isMatch);
                
                if(isMatch!=0){
                    //compare false
                    newUser.save((err, User) => {
                        if(err){
                            res.send(err);
                        }    
                        res.json(User);
                    });
                } else {
                    //// compare false
                }
            });
        });

型号

UserSchema.methods.comparePassword = function(candidatePassword:string,foundPWD:string, cb):void {
      bcrypt.compare(candidatePassword,foundPWD, (err, isMatch) => {
        if (err) return cb(err);
        console.log(foundPWD);
        cb(null, isMatch);    
  });
}

热切希望