bcrypt密码与compare方法比较总是会导致错误

时间:2017-01-15 07:04:39

标签: javascript mongodb express bcrypt mongoose-schema

首先,我知道在我之前已经多次提出过同样的问题,但我无法在StackOverflow上找到的任何问题中找到答案。

我刚刚开始学习express,这是我第一次尝试创建应用程序,包括后端和前端 javascript库(来自PHP世界)。我已经宣布了一个MongoDB模型架构,其中包含一些前期工作和一个将输入的密码与 哈希密码存储在数据库中。除了comparePassword方法永远不会返回匹配的密码外,其他所有内容似乎都运行正常。

我正在使用bcryptjs库进行密码散列和比较,并使用passport库进行身份验证。

用户模型 (models / user.js)

var mongoose            = require('mongoose'),
    Schema              = mongoose.Schema,
    bcrypt              = require('bcryptjs');
    SALT_WORK_FACTOR    = 10;

var userSchema = new Schema({
    //id: ObjectId,
    email: {
        type: String,
        unique: true,
        required: true
    },
    name: {
        type: String,
        required: true
    },
    password: {
        type: String,
        required: true
    }
});

userSchema.pre('save', function(next) { // Hash the password before adding it to the database
    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) {
    var user = this;
    bcrypt.compare(candidatePassword, user.password, function(err, isMatch) {
        console.log(candidatePassword);
        console.log(user.password);
        console.log((candidatePassword === user.password) ? 'passwords match' : 'passwords dont match' );
        return;
        if (err) return cb(null, err);
            cb(null, isMatch);
    });
};
module.exports = mongoose.model('User', userSchema);

身份验证策略 (config / passport.js)

var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var mongoose = require('mongoose');
var User = mongoose.model('User');

passport.use(new LocalStrategy({
        usernameField: 'email'
    },
    function(username, password, done) {
        User.findOne({ email: username }, function (err, user) {
            if (err) { return done(err); }

            if (!user) { // Return if user not found in database
                return done(null, false, {
                    message: 'User not found'
                });
            }

        // It will always output "Incorrect creditentials"
            if (!user.comparePassword(password)) { 
                return done(null, false, {
                    error: true,
                    message: 'Incorrect creditentials'
                });
            }
            return done(null, user); // If credentials are correct, return the user object
        });
    }
));

最后,我的登录路线 (routes / auth.js)

var router = require('express').Router(); // get router instance
var request = require('request');
var passport = require('passport');
var User = require('../../models/user');
var tokenAuth = require('../../middlewares/token');

router.post('/signin', function(req, res) {
    passport.authenticate('local', function(err, user, info){
        var token;

        if (err) { // If Passport throws/catches an error
            res.status(404).json(err);
            return;
        }

        if(user) { // If a user is found
            token = user.generateJwt();
            res.status(200);
            res.json({
                "token" : token
            });
        } else {
            // If user is not found
            res.status(401).json(info);
        }
    })(req, res);

});

module.exports = router;

编辑:

如果我删除了console.log输出:

bcrypt.compare(candidatePassword, user.password, function(err, isMatch) {
            console.log(candidatePassword);
            console.log(user.password);
            console.log((candidatePassword === user.password) ? 'passwords match' : 'passwords dont match' );
            return;
            if (err) return cb(null, err);
                cb(null, isMatch);
        });
    };

并尝试执行回调函数,我将收到以下错误:

cb(null, isMatch);
        ^
TypeError: undefined is not a function
    at D:\project\backend\dist\models\user.js:51:9
    at D:\project\node_modules\bcryptjs\dist\bcrypt.js:297:21
    at D:\project\node_modules\bcryptjs\dist\bcrypt.js:1250:21
    at Object.next [as _onImmediate] (D:\project\node_modules\bcryptjs\dist\bcrypt.js:1130:21)
    at processImmediate [as _immediateCallback] (timers.js:354:15)

编辑2:

所以,我最终能够比较密码,能够console.log密码是否匹配。我能用Promises把它拉下来。现在我不确定如何将该Promise传递给passport处理程序,以便它可以返回路由的用户结果。

这是comparePassword方法:

userSchema.methods.comparePassword = function(candidatePassword) {
    var user = this;

    return new Promise(function(resolve,reject)
    {
        bcrypt.compare(candidatePassword, user.password, function (err, isMatch) {
            // Prevent conflict btween err and isMatch
            if (err)
                reject(new Error("Error checking use password"));
            else
                console.log(isMatch === true ? 'passwords match' : 'passwords dont match');
                return;
                resolve(isMatch);
        });
    });
};

passport.js

passport.use(new LocalStrategy({
        usernameField: 'email'
    },
    function(username, password, done) {
        User.findOne({ email: username }, function (err, user) {
            if (err) { return done(err); }
            // Return if user not found in database
            user.comparePassword(password).then(function(isMatch) {
                return isMatch === true ? user : null; // How to pass the user object to route??
            }).catch(function (err) { // handle possible errors
                return done(err);
            })
        });
    }
));

1 个答案:

答案 0 :(得分:0)

我以为你只是在bcrypt比较中传递回调。确保将plaintextpassword作为参数传递,并将其与db的哈希密码进行比较。

而不是这样做

 if (!user.comparePassword(password)) { 
     return done(null, false, {
         error: true,
                message: 'Incorrect creditentials'
     });
 }

为什么不这样做

user.comparePassword(function (err, match) {
     if (err) throw err;
     if (!match) {
         return done(null, false, {
              error: true,
              message: 'Incorrect creditentials'
         });
     } else {
         // Password match
     }
});

并且在bcrypt比较方法中,更改回调参数,错误必须是第一个而res必须是第二个

userSchema.methods.comparePassword = function(candidatePassword, cb) {
    var user = this;
    bcrypt.compare(candidatePassword, user.password, function(err, isMatch) {
        console.log(candidatePassword);
        console.log(user.password);
        // You shouldn't compare the password directly like this. Let the method handle it and once the response is return (isMatch), pass it as callback param. Comment this line, you don't need it
        //console.log((candidatePassword === user.password) ? 'passwords match' : 'passwords dont match' );
        //return;
        // Prevent conflict btween err and isMatch
        if (err) return cb(err, null);
            cb(null, isMatch);
    });
};

修改

您需要在密码匹配时调用done并传递用户对象

passport.use(new LocalStrategy({
        usernameField: 'email'
    },
    function(username, password, done) {
        User.findOne({ email: username }, function (err, user) {
            if (err) { return done(err); }
            // Return if user not found in database
            user.comparePassword(password).then(function(isMatch) {
                if (isMatch) {
                    return done(null, user);
                } else {
                    return done(null, false);
                }
            }).catch(function (err) { // handle possible errors
                return done(err);
            })
        });
    }
));

在Route Middeware中

我猜你的路线是这样的

app.post('/login',
  passport.authenticate('local', {
    successRedirect: '/loginSuccess',
    failureRedirect: '/loginFailure'
  })
);

app.get('/loginFailure', function(req, res, next) {
    res.send('Failed to authenticate');
});

// Login success should return user object
app.get('/loginSuccess', function(req, res, next) {
  res.send('Successfully authenticated');
});