【问题标题】:Hashed password update with mongoose express使用 mongoose express 更新哈希密码
【发布时间】:2020-09-15 21:35:05
【问题描述】:

我已经查看了很多关于这个问题的讨论,但似乎没有一个对我有帮助。

我使用猫鼬5.5保存用户数据如下图:

我的架构如下所示:

const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const bcrypt = require("bcryptjs");

const userSchema = Schema({

  userName: {
    type: String
  },
  firstName: {
    type: String
  },
  surName: {
    type: String
  },
  password: {
    type: String,
    required: true
  }
});

userSchema.pre('save', async function(next){

try {
  if(!this.isModified('password')){
      return next();
  }
  const hashed = await bcrypt.hash(this.password, 10);
  this.password = hashed;

} catch (err) {
    return next(err);
  }
});

module.exports = user;

我的注册码如下所示:

exports.register = async (req, res, next) => {

try {
    const user = await db.user.create(req.body);
    const {id, username} = user;
    res.status(201).json({user});
    
} catch (err) {
    if(err.code === 11000){
        err.message ='Sorry, details already taken';
    }
    next(err);
  }
};

登录代码如下所示:

exports.login = async (req, res, next) => {

try {
    const user = await db.user.findOne({username: req.body.username});
    const valid = await user.comparePasswords(req.body.password);

    if(valid){

        const token = jwt.sign({id, username}, process.env.SECRET);
        res.json({id, username, token});
    }
    else{
        throw new Error();
    }        
    
} catch (err) {
    err.message = 'Invalid username/password';
    next(err);
  } 
};

注册和登录效果很好,我的挑战是更新密码。我想将当前密码与用户提供的密码进行比较(如登录时),如果有效则更新新密码。

类似这样的:

exports.changepass = async (req, res, next) => {
    const user = await db.user.findOne({username: req.body.username});
    const valid = await user.comparePasswords(req.body.password);

    if(valid){

           " ?? update password and hash ?? "
    }
    else{
        throw new Error();
    }       

}

【问题讨论】:

    标签: mongodb express mongoose


    【解决方案1】:

    如果您使用findOneAndUpdate() 进行更新,请尝试使用pre("findOneAndUpdate") 中间件修改密码,类似于您的pre("save")。每次使用 Model.findOndAndUpate() 更新模型时,都会调用 pre("findOneAndUpdate") 中间件。

    你可以用updateOne()pre("updateOne")做同样的事情

    示例:

    // userSchema--------------------
    ...
    userSchema.pre('save', async function (next) {
        try {
            if (!this.isModified('password')) {
                return next();
            }
            const hashed = await bcrypt.hash(this.password, 10);
            this.password = hashed;
        } catch (err) {
            return next(err);
        }
    });
    
    userSchema.pre('findOneAndUpdate', async function (next) {
        try {
            if (this._update.password) {
                const hashed = await bcrypt.hash(this._update.password, 10)
                this._update.password = hashed;
            }
            next();
        } catch (err) {
            return next(err);
        }
    });
    
    // changepass--------------------
    ...
    if(valid){
    
        //" ?? update password and hash ?? "
        const result = await db.user.findOneAndUpdate(
            { username: req.body.username },
            { password: req.body.newPassword },
            { useFindAndModify: false }
        ); 
    }
    

    【讨论】:

    • 那我该如何使用这个模式呢?就像上面的例子一样..它有效吗?更新
    • @Denn 我已经编辑了代码供您使用。我希望这可以澄清它
    • @Denn 你可以简单地在你的下面添加这段代码。您不必修改任何其他内容。每次使用 db.user.findOneAndUpdate() 时都会调用 userSchema.pre('findOneAndUpdate')
    【解决方案2】:

    我使用中间件Schema.pre('save') 保存和Schema.pre('findOneAndUpdate') 更新数据解决了这个问题。

    在用户架构中,

    // Hashing data before saving into database
    UsersSchema.pre("save", async function (next) {
      try {
        // When password is hashed already, no need to be hashed
        if (!this.isModified("password")) {
          return next();
        }
    
        const hashedPassword = await bcrypt.hash(this.password, 10);
        this.password = hashedPassword;
      } catch (err) {
        return next(err);
      }
    });
    
    // Hashing data before updating into database
    UsersSchema.pre("findOneAndUpdate", async function (next) {
      try {
        if (this._update.password) {
          const hashed = await bcrypt.hash(this._update.password, 10);
          this._update.password = hashed;
        }
        next();
      } catch (err) {
        return next(err);
      }
    });
    

    在用户控制器中,

        try {
        const user = await User.findOneAndUpdate(
          {
            _id
          },
          userInputValue,
          {
            // For adding new user to be updated
            new: true,
            // upsert: true,
            // Active validating rules from Schema model when updating
            runValidators: true,
            context: 'query'
          }
        );
    
        if (!user) return res.status(404).send("User Not Found");
    
        const userData = {
          user: {
            _id: user._id,
            name: user.name,
            email: user.email,
            role: user.role,
            createdAt: user.createdAt
          },
          success: {
            title: 'User Info Update',
            message: `You have updated the user ${user.name}'s info successfully.`
          }
        };
    
        return res.status(200).send(userData);
    
        // res.send(user);
      } catch (err) {
        res.status(500).send(err);
      }
    

    【讨论】:

      【解决方案3】:

      使用此代码:

      schemaName.pre("updateOne", async function(next) {
         try {
            if(this._update.password) {
                this._update.password = await bycrpt.hash(this._update.password, 10);
            }
            next();
        } catch (err) {
            return next(err);
        }
      })
      

      说明:

      当您使用findOneAndUpdate()updateOne()更新时,需要分别使用pre("findOneAndUpdate")pre("updateOne")中间件修改密码。 每次使用Model.findOndAndUpate()Model.updateOne() 更新模型时,都会调用pre("findOneAndUpdate")pre("updateOne") 中间件。

      【讨论】:

      • 你需要解释而不是只写解决方案
      • @FiodorovAndrei 感谢您的建议,我实际上是新来的。使用 findOneAndUpdate() 或 updateOne() 进行更新时,需要分别使用 pre("findOneAndUpdate") 或 pre("updateOne") 中间件修改密码。每次使用 Model.findOndAndUpate() 或 Model.updateOne() 更新模型时,都会调用 pre("findOneAndUpdate") 或 pre("updateOne") 中间件。
      【解决方案4】:

      userSchema.pre('findOneAndUpdate', async function (next) {
        const user = this;
        if (user._update.$set.password) {
          user._update.$set.password = await bcrypt.hash(user._update.$set.password, 8);
        }
        next();
      });

      【讨论】:

        猜你喜欢
        • 2021-01-13
        • 2017-03-08
        • 2014-07-15
        • 1970-01-01
        • 1970-01-01
        • 2012-08-21
        • 2020-04-23
        • 2020-11-09
        • 1970-01-01
        相关资源
        最近更新 更多