首先,我知道在我之前已经多次提出过同样的问题,但我无法在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);
})
});
}
));
答案 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');
});