NodeJS中的密码重置

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/42682923/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 18:04:00  来源:igfitidea点击:

Password Reset In NodeJS

node.jsmongodbexpresspasswordspassport.js

提问by AndrewLeonardi

I have made a set up to update a user's password using NodeJS/Passport. I followed this great guide: http://sahatyalkabov.com/how-to-implement-password-reset-in-nodejs/.

我已经进行了设置以使用 NodeJS/Passport 更新用户的密码。我遵循了这个很棒的指南:http: //sahatyalkabov.com/how-to-implement-password-reset-in-nodejs/

99% of this is working. I had to modify it a bit to include some stripe functionality. I am afraid however I have a critical error somewhere and I cannot find it. The user is currently able to go all the way through the process of having an email sent to them, enter a new password, and be logged in. Another email follows saying their password was successfully updated. All perfect. However. For some reason. The new password is not being SAVED. the user can only sign in with their old password. I have tried everything I can think of to fix this.

这其中的 99% 正在发挥作用。我不得不稍微修改一下以包含一些条带功能。但是我担心我在某处有一个严重错误,我找不到它。用户目前可以完成向他们发送电子邮件、输入新密码并登录的整个过程。随后的另一封电子邮件说他们的密码已成功更新。一切都很完美。然而。因为某些原因。新密码没有被保存。用户只能使用他们的旧密码登录。我已经尝试了我能想到的一切来解决这个问题。

I have had several other programmers look at this and none of them have been able to figure out how in the world its not working.

我让其他几个程序员看过这个,但没有一个人能够弄清楚它到底是如何不起作用的。

Current thought is that the session might not be ending correctly but we tried destroying the session and it still did not work.

当前的想法是会话可能没有正确结束,但我们尝试销毁会话,但它仍然无效。

Any help greatly appreciated.

非常感谢任何帮助。

Full set up:

完整设置:

User Model:

用户模型:

var UserSchema = new mongoose.Schema({
    username:   { type: String, required: true, unique: true },
    password:  String,
    datapoint:  String,
    email:  { type: String, required: true, unique: true },
    resetPasswordToken: String,
    resetPasswordExpires: Date
});


UserSchema.pre('save', function(next) {
  var user = this;
  var SALT_FACTOR = 5;

  if (!user.isModified('password')) return next();

  bcrypt.genSalt(SALT_FACTOR, function(err, salt) {
    if (err) return next(err);

    bcrypt.hash(user.password, salt, null, function(err, hash) {
      if (err) return next(err);
      user.password = hash;
      next();
    });
  });
});

Register New Account(This also has stripe info in it unrelated but could cause the problem.)

注册新帐户(这也有不相关的条带信息,但可能会导致问题。)

var newUser = new User({username: req.body.username, email: req.body.email, datapoint: req.body.datapoint});
      User.register(newUser, req.body.password, function(err, user){


            if(err){
            console.log('Looks like there was an error:' + ' ' + err)
             res.redirect('/login')

          } else {
          passport.authenticate("local")(req, res, function(){

      var user = new User({
      username: req.body.username,
      email: req.body.email,
      password: req.body.password

      })


console.log('creating new account')
console.log('prepping charge')
var token = req.body.stripeToken; // Using Express
var charge = stripe.charges.create({
  amount: 749,
  currency: "usd",
  description: "Example charge",
  source: token,

}, function(err, charge) {
  // asynchronously called
  console.log('charged')
});
            res.redirect('/jobquiz')
             console.log(req.body.datapoint)
             console.log(req.body.email)

          });
          }
      });
});

Set up forgot password posting

设置忘记密码发帖

app.post('/forgot', function(req, res, next) {
  async.waterfall([
    function(done) {
      crypto.randomBytes(20, function(err, buf) {
        var token = buf.toString('hex');
        done(err, token);
      });
    },
    function(token, done) {
      User.findOne({ email: req.body.email }, function(err, user) {
        if (!user) {
        //   console.log('error', 'No account with that email address exists.');
        req.flash('error', 'No account with that email address exists.');
          return res.redirect('/forgot');
        }
console.log('step 1')
        user.resetPasswordToken = token;
        user.resetPasswordExpires = Date.now() + 3600000; // 1 hour

        user.save(function(err) {
          done(err, token, user);
        });
      });
    },
    function(token, user, done) {
        console.log('step 2')


      var smtpTrans = nodemailer.createTransport({
         service: 'Gmail', 
         auth: {
          user: 'myemail',
          pass: 'mypassword'
        }
      });
      var mailOptions = {

        to: user.email,
        from: 'myemail',
        subject: 'Node.js Password Reset',
        text: 'You are receiving this because you (or someone else) have requested the reset of the password for your account.\n\n' +
          'Please click on the following link, or paste this into your browser to complete the process:\n\n' +
          'http://' + req.headers.host + '/reset/' + token + '\n\n' +
          'If you did not request this, please ignore this email and your password will remain unchanged.\n'

      };
      console.log('step 3')

        smtpTrans.sendMail(mailOptions, function(err) {
        req.flash('success', 'An e-mail has been sent to ' + user.email + ' with further instructions.');
        console.log('sent')
        res.redirect('/forgot');
});
}
  ], function(err) {
    console.log('this err' + ' ' + err)
    res.redirect('/');
  });
});

app.get('/forgot', function(req, res) {
  res.render('forgot', {
    User: req.user
  });
});

Set up changing password post

设置更改密码帖子

app.get('/reset/:token', function(req, res) {
  User.findOne({ resetPasswordToken: req.params.token, resetPasswordExpires: { $gt: Date.now() } }, function(err, user) {
      console.log(user);
    if (!user) {
      req.flash('error', 'Password reset token is invalid or has expired.');
      return res.redirect('/forgot');
    }
    res.render('reset', {
     User: req.user
    });
  });
});




app.post('/reset/:token', function(req, res) {
  async.waterfall([
    function(done) {
      User.findOne({ resetPasswordToken: req.params.token, resetPasswordExpires: { $gt: Date.now() } }, function(err, user, next) {
        if (!user) {
          req.flash('error', 'Password reset token is invalid or has expired.');
          return res.redirect('back');
        }


        user.password = req.body.password;
        user.resetPasswordToken = undefined;
        user.resetPasswordExpires = undefined;
        console.log('password' + user.password  + 'and the user is' + user)

user.save(function(err) {
  if (err) {
      console.log('here')
       return res.redirect('back');
  } else { 
      console.log('here2')
    req.logIn(user, function(err) {
      done(err, user);
    });

  }
        });
      });
    },





    function(user, done) {
        // console.log('got this far 4')
      var smtpTrans = nodemailer.createTransport({
        service: 'Gmail',
        auth: {
          user: 'myemail',
          pass: 'mypass'
        }
      });
      var mailOptions = {
        to: user.email,
        from: 'myemail',
        subject: 'Your password has been changed',
        text: 'Hello,\n\n' +
          ' - This is a confirmation that the password for your account ' + user.email + ' has just been changed.\n'
      };
      smtpTrans.sendMail(mailOptions, function(err) {
        // req.flash('success', 'Success! Your password has been changed.');
        done(err);
      });
    }
  ], function(err) {
    res.redirect('/');
  });
});

采纳答案by Anh Cao

I didn't (or haven't) find any problem with your code, but I have a suggestion to trace the bug.

我没有(或没有)发现您的代码有任何问题,但我有一个建议来跟踪错误。

This block of code is risky. You may accidentally update the password field and trigger the rehash password process.

这个代码块是有风险的。您可能会不小心更新密码字段并触发重新哈希密码过程。

UserSchema.pre('save', function(next) {
   var user = this;
   var SALT_FACTOR = 12; // 12 or more for better security

   if (!user.isModified('password')) return next();

   console.log(user.password) // Check accident password update

   bcrypt.genSalt(SALT_FACTOR, function(err, salt) {
      if (err) return next(err);

      bcrypt.hash(user.password, salt, null, function(err, hash) {
         if (err) return next(err);
         user.password = hash;
         next();
      });
   });
});

Put a console.logright after the if (!user.isModified('password'))to check for unexpected password update. Now retry forget the password and see if any bug in there.

console.logif (!user.isModified('password'))检查意外的密码更新之后放一个右键。现在重试忘记密码,看看那里是否有任何错误。

*TD;LR Separate update password into a new method instead of putting it in the pre-save since you may accidentally update a new password along with other fields

*TD;LR 将更新密码分离到一个新方法中,而不是将其放在预先保存中,因为您可能会不小心将新密码与其他字段一起更新

*Update: Thanks #imnsfor suggesting a better SALT_FACTOR number.

*更新:感谢#imns提出更好的 SALT_FACTOR 数字。

回答by mertje

I think the issue could be in the hash function. Tried duplicating you code into a simpler but similar experiment on my computer.

我认为问题可能出在哈希函数中。尝试在我的计算机上将您的代码复制到一个更简单但类似的实验中。

As the bcrypt docs state here https://www.npmjs.com/package/bcrypt#to-hash-a-password

正如这里的 bcrypt 文档所述https://www.npmjs.com/package/bcrypt#to-hash-a-password

The hash function only takes in 3 arguments, you send in 4. Whereas the third argument in your case is null.

哈希函数只接受 3 个参数,您发送 4 个。而您的情况下的第三个参数是null

Here is some code to illustrate the issue and the, hopefully, solution

这是一些代码来说明问题和希望的解决方案

Inside the salting callback

在盐渍回调中

bcrypt.hash(user.password, salt, null, function(err, hash) {
  if (err) return next(err);
  user.password = hash;
  next();
});

But change the third argument to be the callback function instead.

但是将第三个参数改为回调函数。

bcrypt.hash(user.password, salt, function(err, hash) {
  if (err) return next(err);
  user.password = hash;
  next();
});

回答by user9348495

I had the same issue just delete the null parameter from this line:

我遇到了同样的问题,只是从这一行中删除了 null 参数:

bcrypt.hash(user.password, salt, null, function(err, hash) {

bcrypt.hash(user.password, salt, null, function(err, hash) {

回答by Gaurav

I already used this code in my current project, and its working fine, I saw a small error in your code in function UserSchema.pre('save', function(next). when you hash the password with bcrypt.hashthen it took four arguments but there are only three argument in my code like

我已经在我当前的项目中使用了此代码,并且它工作正常,我在 function 中的代码中看到了一个小错误UserSchema.pre('save', function(next)。当您使用散列密码时,bcrypt.hash它需要四个参数,但我的代码中只有三个参数,例如

schema.pre('save', function(next) {
    var user = this;
    var SALT_FACTOR = 5;

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

    bcrypt.genSalt(SALT_FACTOR, function(err, salt) {
        if(err){
            return next(err);
        }
        bcrypt.hash(user.password, salt, function(err, hash) {
            if(err){
                return next(err);
            }
            user.password = hash;
            next();
        });
    });
});

Third argument must be callback function see document for bcrypt

第三个参数必须是回调函数,请参阅bcrypt 的文档