温馨提示:nodejs内核才是王道,不是特别需要,第三方模块用到哪看到哪就行
passport是做登录验证的中间件,必须添加策略才能工作;passport-local是本地验证策略。但使用策略前需要对策略进行配置。passport-local-mongoose是一种面向mongoose验证策略,已经配置好了,能直接用(我这么理解,因为使用passport-local-mongoose并没有配置过策略)。了解更多看某位前辈的这篇文章
官方简介:
Passport-Local Mongoose is a Mongoose plugin that simplifies building
username and password login with Passport.
官方还给了个Michael Herman 的例子(请戳User Authentication With Passport.js)简单demo了下使用这些插件的使用,并实现了注册、登录的基本功能。
// modles/user.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var UserSchema = new Schema({
username: {type: String, index: {unique: true}},
name:String,
password: String,
avatar: {
type: String,
default: '/images/default-avatar.jpeg'
},
title: {
type: String,
default: '未命名博客'
},
description: {
type: String,
default: '博主很懒,还没有添加任何描述……'
}
});
var passportLocalMongoose = require('passport-local-mongoose');
//给UserSchema添加了一个插件,使得User的Modle拥有了一些有关验证和加密的方法。
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model('User', UserSchema);
//app.js
var passport = require('passport');
var User = require('./models/user');
passport.use(User.createStrategy());//这里为用户验证指定了使用用户名与密码进行验证(Basic)的验证策略。
// 当然,passport-local-mongoose还提供了其他的验证策略如:OAuth、Digest、Anonymous等。
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
//上面这两个方法告诉passport如何将一个User对象序列化为cookie中的值,以及如何从cookie中的值反序列化生成User对象。
app.use(passport.initialize());//passport初始化
app.use(passport.session());//引入passport的session。这样,被配置好的passport将会在用户请求到来时检查cookie并填充req.user对象;在用户登录成功后相应地设置cookie。
官方还给的Michael Herman 的例子并没有提到怎么修改密码。
经过一番折腾算是实现了这个功能:
router.route('/changepwd')
.post(function(req,res,next) {
//这里简单,如果req.user不存在说明没登录,重定向到登录页
if (!req.user) {
console.log(req.session);
console.log(req.cookies);
return res.redirect('/account/login');
}
var password = req.body.password || '';//客户端输入的旧密码
var password1 = req.body.password1 || '';//客户端输入的新密码
//这里进行验证,这个回调函数的第一个参数是null,不知道为什么要这个参有什么用
req.user.authenticate(password, function (noIdea,isPwdcorrect,err) {
if (!isPwdcorrect) {
console.log(err);
//控制台的错误输出,可以看到是“IncorrectPasswordError”:
/*{ [IncorrectPasswordError: Password or username are incorrect]
name: 'IncorrectPasswordError',
message: 'Password or username are incorrect' }
*/
return res.status(400).end('旧密码输入错误!');
}
else{
console.log("密码验证通过");
}
});
//修改密码
//(这一段代码来自http://pepho.blog.163.com/blog/static/13805620160213354105)
req.user.setPassword(password1, function (err) {
if (err) {
console.log(err);
next(err);
} else {
req.user.save(function (err) {
if (err) {
console.log(err);
}
console.log('密码修改成功');
res.status(201).end("修改成功");
});
}
});
});
关键在于不知道传上来的密码如何做验证。以前都是做的小玩意都是明文存储密码的,校验也好进行,对于数据库中的密文感到无从下手,一番寻找,其实passport-local-mongoose也是做了这个功能的,那就是authenticate(password, callback),但我不知道怎么用(我是JS小白请别鄙视我= =)。
翻看源码才知道这个函数是调用了下面这个函数的:
// node_modules/passport-local-mongoose/index.js
//npmpassport-local-mongooseAPI对这个函数描述很简单,不看源码搞不懂是怎么用= =。
//当验证失败时,回调函数里有三个参数:null,false,error;
//当验证成功时,回调函数有两个参数:null,user。
function authenticate(user, password, cb) {
if (options.limitAttempts) {
var attemptsInterval = Math.pow(options.interval, Math.log(user.get(options.attemptsField) + 1));
var calculatedInterval = (attemptsInterval < options.maxInterval) ? attemptsInterval : options.maxInterval;
if (Date.now() - user.get(options.lastLoginField) < calculatedInterval) {
user.set(options.lastLoginField, Date.now());
user.save();
return cb(null, false, new errors.AttemptTooSoonError(options.errorMessages.AttemptTooSoonError));
}
if (user.get(options.attemptsField) >= options.maxAttempts) {
return cb(null, false, new errors.TooManyAttemptsError(options.errorMessages.TooManyAttemptsError));
}
}
if (!user.get(options.saltField)) {
return cb(null, false, new errors.NoSaltValueStoredError(options.errorMessages.NoSaltValueStoredError));
}
pbkdf2(password, user.get(options.saltField), function(err, hashRaw) {
if (err) {
return cb(err);
}
var hash = new Buffer(hashRaw, 'binary').toString(options.encoding);
if (scmp(hash, user.get(options.hashField))) {
if (options.limitAttempts) {
user.set(options.lastLoginField, Date.now());
user.set(options.attemptsField, 0);
user.save();
}
return cb(null, user);
} else {
if (options.limitAttempts) {
user.set(options.lastLoginField, Date.now());
user.set(options.attemptsField, user.get(options.attemptsField) + 1);
user.save(function(saveErr) {
if (saveErr) { return cb(saveErr); }
if (user.get(options.attemptsField) >= options.maxAttempts) {
return cb(null, false, new errors.TooManyAttemptsError(options.errorMessages.TooManyAttemptsError));
} else {
return cb(null, false, new errors.IncorrectPasswordError(options.errorMessages.IncorrectPasswordError));
}
});
} else {
return cb(null, false, new errors.IncorrectPasswordError(options.errorMessages.IncorrectPasswordError));
}
}
});
}
看得到返回的cb的第一个参数都是null,对此感到费解。有机会要问问大牛们。