【译】Passport 文档(一)入门

本系列一共4篇文章


下面正文开始啦 ^_^

www.passportjs.org/docs/downlo… 原文地址

概览

Passport 是 NodeJS 的认证中间件。他的唯一设计目的是:验证请求。书写模块化的、封装代码是一种美德,所以 Passport 将除了验证请求之外的功能都分发给应用程序来实现。关注点分离使代码能够更加整洁、可维护,同时也使 Passport 能够极易集成到应用中。

现在 Web 程序,认证有多种形式。传统的,用户通过用户名、密码登录。随着社交网络使用上升,使用 OAuth 的单点登录,例如 Facebook 或者 Twitter 已经成为了一种流行的认证方式。暴露一个 API 的服务通常需要基于 token 的证书来保护访问。

Passport 认识到每个应用有自己独特的认证需求。认证机制,也被成为策略,被打包成单独的模块。应用能够选择采用的策略,无需创建不需要的依赖。

不管认证的复杂性,代码能够不变的复杂。

1
js复制代码app.post('/login', passport.authentication('local', {successRedirect: '/', failureRedirect: '/login'}));

安装

1
js复制代码npm install passport

认证

认证请求就像调用 passport.authenticate() 和指定采用哪种策略一样简单。authenticate() 的函数签名是标准的 Connect 中间件,所以能够方便的作为路由中间件在 Express 应用中使用。

1
2
3
4
5
6
7
8
js复制代码app.post('/login',
passport.authenticate('local'),
function(req, res) {
// 如果这个函数被调用,说明认证成功。
// `req.user` 包含认证的 user
res.redirect('/user', + req.user.username);
}
)

默认情况下,如果认证失败,Passport 将返回 401 Unauthorized,后续其他的路由处理器将不会执行。如果认证成功,下个处理器将调用,req.user 属性将设置为认证 user。

注意:在路由使用策略时,一定要预先配置。继续阅读详细配置 todo 章节。

重定向

重定向通常是在认证请求之后发出的。

1
2
3
4
5
6
js复制代码app.post('/login',
passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login',
})
)

这种情况下,重定向选项将覆盖默认行为。若成功认证,用户将重定向到主页。若认证失败,用户将重定向返回到登录页去再次尝试。

即时消息(Flash Messages)

重定向通常和即时消息结合来展示用户的状态信息。

1
2
3
4
5
6
7
js复制代码app.post('/login',
passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login',
failureFlash: true
});
);

设置 failureFlash 选项为 true,指示 Passport 去发送 来自策略验证回调的 error 信息。这通常是最好的方法,因为验证回调能够最准确的判断为什么认证失败。

或者,即时消息能够自定义设置。

1
2
3
js复制代码passport.authenticate('local', {
failureFlash: '无效用户名或者密码。'
})

successFlash 选项能够发送一个 success 的消息,在认证成功的时候。

1
2
3
js复制代码passport.authenticate('local', {
successFlash: '欢迎!'
})

注意:使用即时消息需要 req.flash() 函数。Express 2.x 提供了这个功能,不过 Express 3.x 移除了。在使用 Express 3.x 时,建议使用 connect-flash 中间件,它提供了这个功能。

禁止 Sessions

成功授权后,Passport 将建立一个持久的登录 session。对于用户通过浏览器访问 web 应用的场景这是有用的。然后,其他情况下,不需要 session 支持。例如,API 服务器通常需要每个请求携带凭证。这种情况下,session 支持能够通过设置 session 选项为 false 来安全的禁用。

1
2
3
4
5
6
js复制代码app.get('/api/users/me',
passport.authenticate('basic', { session: false }),
function(req, res) {
res.json({id: req.user.id, username: req.user.username});
}
)

自定义回调

如果内部选项不足够处理认证请求,自定义回调能够让应用处理成功和失败的情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
js复制代码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);
})

这个例子中,注意 authenticate() 是在路由处理器中被调用,而不是作为路由中间件。这通过闭包给了 reqres 对象回调权限。

如果认证失败,user 将被设置为 false。如果发生异常,err 将被设置。一个可选的 info 参数将传入,包括策略验证回调所提供的附加的详细信息。

这个回调能够使用提供的参数处理预期的认证结果。注意,当使用自定义回调时,需要应用来建立 session(通过调用 req.login()) 和发送响应。

配置

使用 Passport 来认证需要配置三个方面:

1、认证策略
2、应用中间件
3、Session(可选的)

策略

Passport 使用被称为策略的东西来认证请求。策略从验证用户名、密码,使用 OAuth 委托认证或者使用 OpenID 联合认证。

在让 Passport 认证请求前,应用使用的某个策略(或者某些策略)必须要先配置。

策略和他们的配置通过 use() 函数提供。例如,下面的例子使用 LocalStrategy 来进行用户名、密码认证。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
js复制代码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)
})
}
))

验证回调

这个例子引入了一个重要的概念。策略需要一个称为回调的东西。验证回调的目的是找到拥有一套凭证的用户。

当 Passport 认证请求时,它解析请求中的凭证。然后将凭证作为调用回调函数的参数,这个例子中就是 usernamepassword。如果凭证有效,回调函数将调用 done 将已认证的用户的信息传入 Passport。

1
js复制代码return done(null, user);

如果验证失败(本例中,比如密码错误),done 函数应该传入 false 而不是用户信息来表明认证失败。

1
js复制代码return done(null, false);

可以提供额外的消息来表明失败原因。这对于展示即时消息,来提示用户再次尝试很有用。

1
js复制代码return done(null, false, { message: '密码错误' });

最后,当验证凭证时发生异常(例如,数据库服务不可用),在常规的 Node 操作中 done 应该被调用来传入错误信息。

1
js复制代码return done(err);

注意,对于区分两种能够发生失败的情况是很重要的。后者是服务端异常,这种情况下 err 被设置为非 null 的值。在服务器正常运行时,认证失败也是很自然的情况。确认 err 包含 null,使用最后一个参数传递详细信息。

验证回调通过委派的方式使 Passport 可以无需数据库支持。应用可以自己决定如何存储用户信息,没有验证层强加的任何假设。

中间件

在基于 Connect 或者 Express 的应用中,需要使用 passport.initialize() 中间件来初始化 Passport。如果你的应用使用了持久化登录 Session,passport.session() 中间件也需要使用。

1
2
3
4
5
6
7
8
9
js复制代码app.configure(function() {
app.use(express.static('public'));
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.session({ secret: 'keyboard cat' }));
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
})

注意,开启 session 支持完全是可选的,尽管建议将其用于大多数应用中。如果开启,确认在 passport.session() 前使用 session(),从而确保登录 session 能够按正确的顺序存储。

在 Express 4.x 中,Connect 中间件不再包含于 Express 核心模块中,app.configure() 也被移除了。相同的中间件能够在 npm 模块找到。

1
2
3
4
5
6
7
8
js复制代码var session = require('express-session'),
bodyParser = require('body-parser');

app.use(express.static('public'));
app.use(session({ secret: 'cats' }));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(passport.initialize());
app.use(passport.session());
sessions 会话

在常规的 web 应用中,用于认证用户的凭证仅在登录请求时被发送。如果认证成功,一个session 将被建立和保持,通过设置在浏览器中的 cookie。

每个随后的请求将不再包含凭证,但是会有唯一的 cookie 来确认 session。为了支持登录 session,Passport 将序列化和反序列化 user 实例经由 session。

1
2
3
4
5
6
7
8
9
js复制代码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 中,从而保持存储在 session 中的数据量较小。当后续请求到达时,这个 ID 将用来找到存储在 req.user 中的 user。

序列化和反序列化逻辑由应用来提供,允许应用选择一个合适的数据库和(或者)对象mapper,无需认证层强加。

用户名 和 密码

最广泛使用的网站认证用户的方式是通过用户名和密码。对这种机制的支持是通过提供 passport-local 模块。

安装

1
js复制代码npm install passport-local

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
js复制代码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);
})
}
))

这个本地认证的验证回调接受 usernamepassword 参数,通过应用的登录表单提交上来的。

表单

Web 页面的一个表单,允许用户输入他们凭证然后登录。

1
2
3
4
5
6
7
8
9
10
11
12
13
html复制代码<form action="/login" method="post">
<div>
<label>Username:</label>
<input type="text" name="username"/>
</div>
<div>
<label>Password:</label>
<input type="password" name="password"/>
</div>
<div>
<input type="submit" value="Log In"/>
</div>
</form>

路由

登录表单通过 POST 方法提交到服务器。使用 local 策略的 authenticate 函数来处理登录请求。

1
2
3
4
5
6
7
js复制代码app.post('/login',
passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login',
failureFlash: true
})
)

设置 failureFlash 选项为 true 表明 Passport 使用验证回调提供的 message 选项来发送一个 error 消息。这对于提示用户再试一次很有用。

参数

默认的,LocalStrategy 预期在参数中找到命名为 usernamepassword 的凭证。如果你的网站更喜欢用其他字段命名,有可用的选项支持修改默认值。

1
2
3
4
5
6
7
8
js复制代码passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'passwd',
},
function(username, password, done) {
// ...
}
))

OpenID

OpenID 是一个联合认证的开放标准。当访问网站时,用户使用 OpenID 登录。用户通过他们选择 OpenID 提供者(它发出一个断言来确认用户身份)来认证。网站验证这个断言来让用户登录。

OpenID 的支持通过 passport-openid 模块提供。

安装

1
js复制代码npm install passport-openid

配置

当使用 OpenID 时,返回地址和领域必须设置。 returnURL 是用户在使用 OpenID 提供者认证后重定向的地址。realm 表明 URL空间中验证有效的部分。通常它会是网站的根 URL。

1
2
3
4
5
6
7
8
9
10
11
12
13
js复制代码var passport = require('passport'),
OpenIDStrategy = require('passport-openid').Strategy;

passport.use(new OpenIDStrategy({
returnURL: 'http://www.example.com/auth/openid/return',
realm: 'http://www.example.com/'
},
function(identifier, done) {
User.findOrCreate({openId: identifier}, function(err, user) {
done(err, user);
})
}
))

OpenID认证的验证回调接受一个 identifier 参数,包含用户的声明识别码。

表单

web 页面的表单,允许用户输入 OpenID 然后登录。

1
2
3
4
5
6
7
8
9
html复制代码<form action="/auth/openid" method="post">
<div>
<label>OpenID:</label>
<input type="text" name="openid_identifier"/><br/>
</div>
<div>
<input type="submit" value="Sign In"/>
</div>
</form>

路由

OpenID 认证需要2个路由。第一个路由接受表单提交中包含的 OpenId 识别码。认证期间,用户将被重定向到 OpenID 提供者。第二个路由是用户在使用 OpenID 提供者认证后,将返回的 URL。

1
2
3
4
5
6
7
8
9
10
11
12
13
js复制代码// 接受 OpenID 识别码,将用户重定向到 OpenID 提供者处去认证。完成后,提供者将用户重定向到应用:
// /auth/openid/return
app.post('/auth/openid', passport.authenticate('openid'));

// OpenID 提供者已经将用户重定向到应用。
// 通过验证断言来完成认证过程。如果有效,用户将登录。
// 否则,认证失败。
app.get('/auth/openid/return',
passport.authenticate('openid', {
successRedirect: '/',
failureRedirect: '/login',
})
)

个人资料交换

OpenID 能够可选择的设置为取回已认证用户的个人信息。个人资料交换通过设置 profile 选项为 true 开启。

1
2
3
4
5
6
7
8
9
js复制代码passport.use(new OpenIDStrategy({
returnURL: 'http://www.example.com/auth/openid/return',
realm: 'http://www.example.com/',
profile: true
},
function(identifier, profile, done) {
// ...
}
))

当个人资料交换开启后,验证回调函数签名接收额外的 profile 参数,包含 OpenID 提供者提供的用户个人信息;通过 User Profile 来了解更多信息。

OAuth

OAuth 是个标准协议,它允许用户授权 API 使用权给网站、桌面程序或者移动应用。一旦被授权,被授权的应用能够代表用户使用 API。OAuth 也已经成为流行的委托授权机制。

OAuth 有两种主要形式,两种都被广泛的部署。

OAuth 初始版本被一群组织松散的 Web 开发者开发作为开放标准。他们开发了 OAuth 1.0,被 OAuth 1.0a 取代。这项工作现在被 IEFT 作为 RFC 5849 准备被标准化。

最近的工作 – 专注定义 OAuth 2.0,已经被 web 授权协议工作组 承担。由于长期的标准化工作,提供者已经开始部署符合各种草案的实现,每种语义略有不同。

谢天谢地,Passport 隔离了应用处理 OAuth 变体的复杂性。许多情况下,特定策略的提供者能够被使用。而不是下面描述的通用策略。这减少了必须的配置,并且能容纳任何提供者的特定怪癖。查看 Facebook, Twitter 或者列表中的提供者的首选用法。

OAuth 的支持通过 passport-oauth 模块实现

安装

1
js复制代码npm install passport-oauth

OAuth 1.0

OAuth 1.0 是包含多个步骤的代理认证策略。首先,需要获得请求 token。然后,用户被重定向到服务提供者处授权。最后,授权后,用户被重定向回应用并且请求 token 能够用来交换访问 token。请求访问的应用程序(称为消费者)由消费者 key 和 消费者 secret 标识。

配置

当使用通用 OAuth 策略时,key, secret 和 endpoints 作为选项定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
js复制代码var passport = require('passport'),
OAuthStrategy = require('passport-oauth').OAuthStrategy;

passport.use('provider', new OAuthStrategy({
requestTokenURL: 'https://www.provider.com/oauth/request_token',
accessTokenURL: 'https://www.provider.com/oauth/access_token',
userAuthorizationURL: 'https://www.provider.com/oauth/authorize',
consumerKey: '123-456-789',
consumerSecret: 'shhh-its-a-secret',
callbackURL: 'https://www.example.com/auth/provider/callback'
},
function(token, tokenSecret, profile, done) {
User.findOrCreate(..., function(err, user) {
done(err, user);
})
}
))

基于 OAuth 策略的验证回调接受 tokentokenSecretprofile 参数。token 是访问 token,tokenSecret 是它对应的秘钥。profile 包含服务提供者提供的用户个人信息。通过 User Profile 了解更多信息。

路由

OAuth 认证需要2个路由。第一个路由发起一个 OAuth 交换和重定向用户到服务提供者处。第二个路由是个URL,用户在提供者认证后重定向的 URL。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
js复制代码// 重定向用户到用于认证的 OAuth 提供者处。当认证完成后,
// 提供者将重定向用户回到应用:
// /auth/provider/callback
app.get('/auth/provider', passport.authenticate('provider'));

// OAuth 提供者已经重定向用户回到应用。
// 通过获取访问 token 来结束认证过程。如果已经授权,用户将登录。
// 否则,认证失败。
app.get('/auth/provider/callback',
passport.authenticate('provider', {
successRedirect: '/',
failureRedirect: '/login'
})
)

链接

在网页中可以放置一个链接或者按钮,它们在点击时将开始认证过程。

1
html复制代码<a href="/auth/provider">使用 OAuth 提供者登录</a>

OAuth 2.0

OAuth 2.0 是 OAuth 1.0 的接班人,被设计来克服早期版本的已知缺陷。认证流程本质上是一样的。用户首先被重定向到服务提供者处授权,授权后,用户携带一个能够获取访问 token 的码被重定向回应用。请求访问的应用(作为客户端)由 ID 和 秘钥标识。

配置

使用通用 OAuth 2.0 策略时,client ID,client secret 和 endpoints 作为选项定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
js复制代码var passport = require('passport'),
OAuth2Strategy = require('passport-oauth').OAuth2Strategy;

passport.use('provider', new OAuth2Strategy({
authorizationURL: '',
tokenURL: '',
clientID: '',
clientSecret: '',
callbackURL: '',
},
function(accessToken, refreshToken, profile, done) {
User.findOrCreate(..., function(err, user) {
done(err, user);
})
}
))

基于 OAuth 2.0 策略的验证回调接受 accessTokenrefreshTokenprofile 参数。refreshToken 能够用来获取新的访问 token,也有可能是 undefined 如果提供者不发行刷新 token。profile 将包含服务提供者提供的用户个人信息。查看 User Profile 了解更多信息。

路由

OAuth 2.0 认证需要两个路由。第一个路由重定向用户到服务提供者。第二个路由是个 URL,用户在提供者处认证后重定向的URL。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
js复制代码// 重定向用户到 OAuth 2.0 认证提供者。
// 认证完成后,提供者将重定向用户返回应用:
// /auth/provider/callback
app.get('/auth/provider', passport.authenticate('provider'));

// OAuth 2.0 提供者已经重定向用户到应用中。
// 通过尝试获取访问 token 来完成认证过程。
// 如果已授权,用户将登录。
// 否则,认证失败。
app.get('/auth/provider/callback',
passport.authenticate('provider', {
successRedirect: '/',
failureRedirect: '/login',
})
)

作用域

当使用 OAuth 2.0 请求访问时,访问的作用域通过 scope 选项控制。

1
2
3
js复制代码app.get('/auth/provider',
passport.authenticate('provider', { scope: 'email' })
)

能够使用数组定义多个作用域。

1
2
3
js复制代码app.get('/auth/provider',
passport.authenticate('provider', { scope: ['email', 'sms'] })
)

scope 选项的值时提供者特定的。详情参考提供者文档,了解支持的作用域。

链接

Web 页面的链接或者按钮,当点击的时候能够开始认证过程。

1
js复制代码<a href="/auth/provider">使用 OAuth 2.0 提供者登录</a>

用户个人信息

当使用第三方服务,例如 Facebook 或者 Twitter 认证时,用户个人信息通常会可用。每个服务倾向于有个不同的方式编码这些信息。为了更易集成,Passport 尽可能规范化个人信息。

规范化个人信息符合 [Joseph Smarr][schema-author] 建立的联系模式。下表概述了可用的公共字段。

provider {String}

  提供者,用户认证的地方(facebook, twitter, 等)。

id {String}

  用户的唯一标识,通常由服务提供者生成。

displayName {String}

  用户名,适合展示

name {Object}

familyName 用户的 family name,或者大多数西方语言的 “last name”。

givenName 用户的 given name,或者大多数西方语言的 “first name”。

middleName 用户的 middle name。

emails {Array} [n]

value {String} 地址的 email 地址。

type {String} email 地址的类型(家里、工作单位,等)。

photos {Array} [n]

value {String} 图片地址

注意,上面的字段不是全部都能从每个服务提供者处获得。一些提供者可能包含没在这列出的其他信息。参考特定提供者的文档来了解更多详细信息。

参考

感谢阅读

感谢你阅读到这里,翻译的不好的地方,还请指点。希望我的内容能让你受用,再次感谢。by llccing 千里

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%