[转]passport.js学习笔记

 

passport

概述

passport.js是Nodejs中的一个做登录验证的中间件,极其灵活和模块化,并且可与Express、Sails等Web框架无缝集成。Passport功能单一,即只能做登录验证,但非常强大,支持本地账号验证和第三方账号登录验证(OAuth和OpenID等),支持大多数Web网站和服务。

策略(Strategy)

策略是passport中最重要的概念。passport模块本身不能做认证,所有的认证方法都以策略模式封装为插件,需要某种认证时将其添加到package.json即可。

策略模式是一种设计模式,它将算法和对象分离开来,通过加载不同的算法来实现不同的行为,适用于相关类的成员相同但行为不同的场景,比如在passport中,认证所需的字段都是用户名、邮箱、密码等,但认证方法是不同的。关于策略模式,本文不详细展开,想了解的推荐阅读Javascript中的策略模式,或者更广泛意义上的策略模式

依据策略模式,passport支持了众多的验证方案,包括Basic、Digest、OAuth(1.0,和2.0的三种实现)、Bearer等。

passport和everyauth

Nodejs中做登录验证的有不少,我听说过的有connect-auth、everyauth、passport,以及Mongoose的插件mongoose-auth。

其中passport和everyauth用的比较多。

everyauth也是基于策略模式,但支持的第三方网站和服务比passport要少些。另外everyauth还涉及到view/route和database,耦合程度比passport高,而passport则更专注。

值得一提的是,passport的作者声称正因为他看到everyauth不能满足他的一些需求所以创建了passport,不过貌似现在everyauth改进了其中的部分缺陷;everyauth的作者则声称它的建立是基于connect-auth的不足上的。

另外这里有讲到选择适合的Node.js授权认证策略,也讲到了两者的一些不同。

依赖和安装

环境依赖

首先你需要Nodejs,然后数据库用来存储用户数据;另外passport作为中间件,需要依赖Express和Connect,还有由于Express 3.x将一些中间件分离出去,因此你还需要先安装它们。其中express-flash是用于显示提示信息的中间件,是可选的,如果需要用到passport中的提示,则需要安装。

具体的依赖有:

  • Express:web框架。或其他支持的框架。
  • Connect:中间件框架。
  • cookie-parser:Connect的cookie解析中间件。
  • express-session:Connect的session解析中间件,依赖于cookie-parser。
  • express-flash:express的消息提示中间件,可选,但一般情况下都需要装。

安装和配置

你最少需要安装一个passport策略来使用它,一般而言本地验证策略passport-local是必装的。

npm install passport
npm install passport-local

安装完成后需要配置中间件,一般的顺序如下:

var express = require('express'); var cookieParser = require('cookie-parser'); var session = require('express-session'); var flash = require('express-flash'); var passport = require('passport'); ... app.use(cookieParser()); app.use(session({...})); app.use(passport.initialize()); app.use(passport.session()); app.use(flash())

其中重要的是app.use()部分,express中的中间件顺序很重要,注意不要弄错,除非你知道不同中间件间的准确依赖关系。

基本用法

passport用的比较多的有local本地验证和OAuth验证,这里讲一下两者的使用。

你也可以看看张丹写的这两篇,Express结合Passport实现登陆认证Passport实现社交网络OAuth登陆,里面的示例覆盖了基本的用法,本文也参考了其中的一些例子。

local本地验证

本地验证默认使用用户名和密码来进行验证。

配置策略

在做验证之前,首先需要对策略进行配置,官方的示例如下:

var passport = require('passport')   , LocalStrategy = require('passport-local').Strategy; passport.use(new LocalStrategy(   function(username, password, done) {     User.findOne({ username: username }, function(err, user) {       if (err) { return done(err); }       if (!user) {         return done(null, false, { message: '用户名不存在.' });       }       if (!user.validPassword(password)) {         return done(null, false, { message: '密码不匹配.' });       }       return done(null, user);     });   } ));

其中的User.findOne()是MongoDB风格的语法,意思是从数据库的User集合中查询一条数据,第一个参数是查询条件,后面是callback,一般在callback中进行后续操作。

这里的逻辑很简单,依次检查usernamepassword,如果出错则返回错误信息,如果通过则返回done(null,user)

usernameField

前面说过passport默认使用用户名和密码来验证,但实际上也有很多需要用邮箱来验证的,那么如何实现呢?

passport在策略配置里提供了options参数,用来设置你要验证的字段名称,即usernameField,使用方法如下:

passport.use(new LocalStrategy({     usernameField: 'email',     passwordField: 'passwd'   },   function(username, password, done) {     // ...   } ));

注意,这里的字段名称应该是页面表单提交的名称,即req.body.xxx,而不是user数据库中的字段名称。

将options作为LocalStrategy第一个参数传入即可。

验证回调

passport本身不处理验证,验证方法在策略配置的回调函数里由用户自行设置,它又称为验证回调。验证回调需要返回验证结果,这是由done()来完成的。

在passport.use()里面,done()有三种用法:

  • 当发生系统级异常时,返回done(err),这里是数据库查询出错,一般用next(err),但这里用done(err),两者的效果相同,都是返回error信息;
  • 当验证不通过时,返回done(null, false, message),这里的message是可选的,可通过express-flash调用;
  • 当验证通过时,返回done(null, user)。
密码验证

在张丹的教程里密码是明文存储的,在实际中这当然不行,上面的代码里是user.validPassword(password)方法,这并不是passport添加的,而是需要用户自定义。

一般对密码进行哈希和盐化的Nodejs模块是bcrypt,它提供一个compare方法来验证密码,如何使用它则超出本文的范围,这里就不讲了。

 session序列化与反序列化

验证用户提交的凭证是否正确,是与session中储存的对象进行对比,所以涉及到从session中存取数据,需要做session对象序列化与反序列化。调用代码如下:

passport.serializeUser(function(user, done) {   done(null, user.id); }); passport.deserializeUser(function(id, done) {   User.findById(id, function(err, user) {     done(err, user);   }); });

这里第一段代码是将环境中的user.id序列化到session中,即sessionID,同时它将作为凭证存储在用户cookie中。

第二段代码是从session反序列化,参数为用户提交的sessionID,若存在则从数据库中查询user并存储与req.user中。

这段代码的顺序可以放在passport.use()的前面或后面,但需要在app.configure()之前。

Authenticate验证

做完了上面这些设置,我们终于可以开始做验证了。

app.post('/login',   passport.authenticate('local', { successRedirect: '/',      failureRedirect: '/login',      failureFlash: true }),   function(req, res) {     // 验证成功则调用此回调函数     res.redirect('/users/' + req.user.username);   });

这里的passport.authenticate(‘local’)就是中间件,若通过就进入后面的回调函数,并且给res加上res.user,若不通过则默认返回401错误。

authenticate()方法有3个参数,第一是name,即验证策略的名称,第二个是options,包括下列属性:

  • session:Boolean。设置是否需要session,默认为true
  • successRedirect:String。设置当验证成功时的跳转链接
  • failureRedirect:String。设置当验证失败时的跳转链接
  • failureFlash:Boolean or String。设置为Boolean时,express-flash将调用use()里设置的message。设置为String时将直接调用这里的信息。
  • successFlash:Boolean or String。使用方法同上。

第三个参数是callback。注意如果使用了callback,那么验证之后建立session和发出响应都应该由这个callback来做,passport中间件之后不应该再有其他中间件或callback。以下是代码:

app.get('/login', function(req, res, next) {   passport.authenticate('local', function(err, user, info) {     if (err) { return next(err); }     if (!user) { return res.redirect('/login'); }     req.logIn(user, function(err) {       if (err) { return next(err); }       return res.redirect('/users/' + user.username);     });   })(req, res, next); });
HTTP request操作

注意上面的代码里有个req.logIn(),它不是http模块原生的方法,也不是express中的方法,而是passport加上的,passport扩展了HTTP request,添加了四种方法。

  • logIn(user, options, callback):用login()也可以。作用是为登录用户初始化session。options可设置session为false,即不初始化session,默认为true。
  • logOut():别名为logout()。作用是登出用户,删除该用户session。不带参数。
  • isAuthenticated():不带参数。作用是测试该用户是否存在于session中(即是否已登录)。若存在返回true。事实上这个比登录验证要用的更多,毕竟session通常会保留一段时间,在此期间判断用户是否已登录用这个方法就行了。
  • isUnauthenticated():不带参数。和上面的作用相反。
完整示例

基本上passport本地验证的知识点就是这些,下面给出一个相对完整的示例,包括bcrypt的实现,这里借用了nodeclub中的方法,为实现它你需要自己配置hash:

var express = require('express'); var cookieParser = require('cookie-parser'); var session = require('express-session'); var passport = require('passport'); var LocalStrategy = require('passport-local').Strategy; //User模型需自己实现 var User = require('../models/User'); var bcrypt = require('bcrypt'); passport.serializeUser(function(user, done) {   done(null, user.id); }); passport.deserializeUser(function(id, done) {   User.findById(id, function(err, user) {     done(err, user);   }); }); //这里的username可以改成前端表单对应的命名,如: // <form><input type="text" name="hehe">...</form> //则这里将所有的username改为hehe passport.use(new LocalStrategy({ usernameField: 'username' }, function(username, password, done) {   //实现用户名或邮箱登录   //这里判断提交上的username是否含有@,来决定查询的字段是哪一个   var criteria = (username.indexOf('@') === -1) ? {username: username} : {email: username};   User.findOne(criteria, function(err, user) {     if (!user) return done(null, false, { message: '用户名或邮箱 ' +

转载于:https://www.cnblogs.com/chris-oil/p/5991435.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值