使用passport-local-mongoose、passport实现用户验证

曹高轩
2023-12-01

温馨提示: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,对此感到费解。有机会要问问大牛们。

 类似资料: