programing

Mongoose 암호 해싱

abcjava 2023. 5. 11. 21:02
반응형

Mongoose 암호 해싱

저는 mongoose를 사용하여 MongoDB에 계정을 저장할 수 있는 좋은 방법을 찾고 있습니다.

문제는 다음과 같습니다.암호는 비동기식으로 해시됩니다.세터는 동기식으로만 작동하기 때문에 여기서는 작동하지 않습니다.

저는 두 가지 방법을 생각했습니다.

  • 모델의 인스턴스를 만들어 해시 함수의 콜백에 저장합니다.

  • '저장'에 대한 사전 후크 만들기

이 문제에 대한 좋은 해결책이 있습니까?

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

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;

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)

이것은 사용자 Mongoose와 brypt의 좋은 방법이라고 생각합니다.

사용자 모델

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

결과

결과

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

가상 및 인스턴스 메소드를 사용하여 이를 수행하는 또 다른 방법은 다음과 같습니다.

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

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

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

                        }

제 생각에 후크를 사용하는 것이 더 나을 것 같습니다, 제가 발견한 몇 가지 조사 후에.

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

다음과 같이 표시됩니다.

사용 사례:

비동기 기본값

나는 이 솔루션을 선호합니다. 왜냐하면 나는 이것을 캡슐화하고 비밀번호로만 계정을 저장할 수 있기 때문입니다.

사용한.find({email})대신에.findOne({email}).

반드시 사용하십시오..findOne(...)사용자를 가져옵니다.

예:

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

언급URL : https://stackoverflow.com/questions/14588032/mongoose-password-hashing

반응형