【问题标题】:bcrypt password comparing with compare method always results with errorbcrypt 密码与 compare 方法比较总是出错
【发布时间】:2017-05-30 05:55:12
【问题描述】:

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

我最近刚开始学习express,我第一次尝试创建应用程序,无论是后端还是前端 javascript 库(来自PHPworld)。我已经声明了一个 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);
            })
        });
    }
));

【问题讨论】:

    标签: javascript mongodb express bcrypt mongoose-schema


    【解决方案1】:

    我以为你只是在 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 compare方法中,更改回调参数,err必须是第一个,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);
                })
            });
        }
    ));
    

    在路由中间件中

    我猜你的路线是这样的

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

    【讨论】:

    • 我试过这个,行为仍然和我原来的代码一样:如果我尝试控制比赛,它将输出"passwords don't match",如果我删除return并尝试回调我收到一个错误:cb(null, isMatch); TypeError: undefined is not a function
    • 哦。我只是注意到你在console.log 后面加上了return。有什么意义?你想防止错误。您不能像这样使用等效字符串直接比较它,它不起作用。让 bcrypt.compare 处理它并尝试 console.log(isMatch),你会得到什么。
    • 该错误意味着 cb 未被识别为函数。您需要按照我在上面的答案中所述传递回调函数。
    • 请多指教一下好吗?我对 Javascript 比较陌生,因为我主要使用 PHP 和类似的服务器端语言进行编码,并且对那些回调、承诺等有点困惑。
    • 哦,我明白了。创建回调和承诺实际上是为了处理异步。所以,nodejs 是一种异步特性,那么你不能逃避使用这两个。你选择使用哪个回调或承诺。有很多简单的文档解释了回调和承诺的细节。
    猜你喜欢
    • 1970-01-01
    • 2012-10-31
    • 2017-09-16
    • 1970-01-01
    • 1970-01-01
    • 2017-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多