58

私はmongooseを使用してアカウントをMongoDBに保存するための良い方法を探しています。

私の問題は次のとおりです。パスワードは非同期でハッシュされます。セッターは同期的にのみ機能するため、ここでは機能しません。

私は2つの方法について考えました:

  • モデルのインスタンスを作成し、ハッシュ関数のコールバックに保存します。

  • 'save'にプリフックを作成する

この問題に対する良い解決策はありますか?

4

10 に答える 10

164

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 the user to database
testUser.save(function(err) {
    if (err) throw err;
});
    
// fetch the 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
    });
});
于 2013-01-30T01:48:29.013 に答える
19

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;
于 2018-11-22T13:24:51.013 に答える
6

TL;DR-Typescriptソリューション

同じ解決策を探していたが、typescriptを使用してここに到着しました。したがって、上記の問題のTSソリューションに関心のある人のために、これが私が最終的に使用したものの例です。

&& contantsをインポートします:

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

于 2020-07-01T08:23:50.137 に答える
4

これはユーザーMongooseとbcryptによる良い方法だと思います!</ p>

ユーザーモデル

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

結果

結果

于 2018-01-31T07:57:20.133 に答える
2

Mongooseの公式ソリューションでは、verifyPassメソッドを使用する前にモデルを保存する必要があり、混乱を招く可能性があります。次のことはあなたのために働きますか?(私はbcryptの代わりにscryptを使用しています)。

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);
    });
};
于 2015-06-11T16:42:59.443 に答える
1

仮想メソッドとインスタンスメソッドを使用してこれを行う別の方法:

/**
 * 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){});
于 2016-02-01T22:07:52.520 に答える
1

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

于 2019-03-13T07:51:46.157 に答える
1
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

                        }
于 2019-03-25T15:55:03.010 に答える
0

私が見つけたいくつかの研究の後、フックを使用する方が良いと思います

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

それが言うところ:

ユースケース:

非同期のデフォルト

私はこれをカプセル化し、アカウントがパスワードでのみ保存できるようにすることができるので、このソリューションを好みます。

于 2013-01-29T18:40:46.593 に答える
0

.find({email})の代わりに使用しました.findOne({email})。

必ず.findOne(...)ユーザーを取得するために使用してください。

例:

const user = await <user>.findOne({ email });
于 2021-05-22T17:21:38.737 に答える