oauth身份验证方式
用户组列表和信息(UGLI)应用程序开始很好地成形。 现在,您可以通过在“ 具有响应式Web设计的MEAN和UGLI CRUD ”中设置的CRUD屏幕显示您创建的本地内容。 您还可以使用在“ MEAN遇见Meetup.com和微数据 ”中开发的服务来合并来自外部站点的内容。
与公众共享会议信息是该项目的重要组成部分。 但是,作为用户组组长,我也希望将某些活动限制为该组的注册成员。 例如,通过关闭匿名访问并要求登录,我可能会选择在评论我们的演示文稿时保留一些礼貌。 因此,在本期中,您将使用Meetup.com的OAuth服务为UGLI应用提供登录功能。 请参阅下载以获取示例代码。
创建一个新的用户帐号
通过单击Sign up按钮(如图1所示),您的应用程序用户可以创建一个新帐户,该帐户存储在本地MongoDB中。 此功能内置于您的应用中-无需其他编程。
图1. UGLI注册页面
从开发的角度来看,这种默认行为无疑是最简单的解决方案,但是就用户体验而言,它仍有一些不足之处。 您的用户组成员已经在Meetup.com上拥有一个帐户,用于RSVP即将举行的会议。 要求他们创建和维护一组重复的凭据不仅烦人,而且公然违反了“不要自己重复”(DRY)原则。
幸运的是,通过使用的MEAN堆栈,您可以设置使用OAuth和Passport的分布式身份验证和授权解决方案。 简单来说,您的用户可以使用用于登录Meetup.com的相同凭据登录(认证)到UGLI应用程序。 但是,如果没有用户的许可,这是无法完成的。 他们必须允许(授权)UGLI应用使用其Meetup.com凭据。
即使通过OAuth对UGLI应用进行了授权,用户凭据也不会与授权的应用共享。 您不会在UGLI中本地存储一组重复的用户名和密码。 希望获得授权的应用程序(UGLI)将用户重定向到OAuth提供程序(Meetup.com),他们在其中提供其凭据(用户名和密码)。 用户成功认证后,访问令牌将返回给授权的应用程序。
这种方案减少了应用程序中的大量代码和逻辑。 您不再需要担心在服务器上存储加密密码-这是OAuth提供程序要解决的问题。 同样,您不再必须编写算法来强制使用强密码或处理忘记的密码或强制用户定期更改其密码。
因此,OAuth为您的用户提供了更少的密码来记忆,并且为您提供了更少的编写代码。 如果这不是双赢方案的教科书定义,我不知道这是什么。
介绍OAuth和Passport
“您不再需要编写算法来强制使用强密码,也不必处理忘记的密码,也不必强制用户定期更改密码。 ”
OAuth是用于分布式身份验证和授权的开放标准。 它由Twitter和业务合作伙伴Ma.gnolia于2006年开发,旨在促进创建桌面小部件,这些小部件显示来自已验证服务的信息。 从那时起,OAuth被数百个主要网站采用,从Google到Facebook再到Twitter,GitHub,LinkedIn等。 (请参阅著名的OAuth服务提供商列表 。)
Passport是为Node.js编写的OAuth库。 具体来说,它是旨在与Express应用程序无缝集成的中间件。 有140多个Passport插件(称为策略 )可供使用,这些插件是为每个OAuth提供程序量身定制的。
如果您在文本编辑器中打开UGLI应用程序的package.json文件(如清单1所示),则可以看到针对四种主要服务(Facebook,Twitter,LinkedIn和Google)的Passport策略,以及用于直接存储凭据的本地策略。在MongoDB中。
清单1. package.json中的护照策略
"dependencies": {
"passport": "~0.2.0",
"passport-local": "~1.0.0",
"passport-facebook": "~1.0.2",
"passport-twitter": "~1.0.2",
"passport-linkedin": "~0.1.3",
"passport-google-oauth": "~0.1.5"
}
使用现有示例作为指南,您将添加第六种策略,以将Meetup.com纳入UGLI的OAuth提供程序。
安装Meetup.com Passport策略
如果您访问使用Meetup API进行身份验证 ,您将看到Meetup.com提供OAuth服务。 在网上快速搜索meetup.com passport.js strategy
生成指向您正在寻找的库的链接: passport-meetup
。
输入npm install passport-meetup --save
meetup npm install passport-meetup --save
将库下载到node_modules并更新package.json中的依赖项块。
那是容易的部分。 制定好策略后,下一步就是将其合并到注册和登录页面中。
将Meetup.com链接添加到注册和登录页面
在文本编辑器中打开public / modules / users / views / signup.client.view.html。 在文件的顶部,您可以看到指向各种OAuth提供程序的链接(如清单2所示)。
清单2. public / modules / users / views / signup.client.view.html
<h3 class="col-md-12 text-center">Sign up using your social accounts</h3>
<div class="col-md-12 text-center">
<a href="/auth/facebook" class="undecorated-link">
<img src="/modules/users/img/buttons/facebook.png">
</a>
<a href="/auth/twitter" class="undecorated-link">
<img src="/modules/users/img/buttons/twitter.png">
</a>
<a href="/auth/google" class="undecorated-link">
<img src="/modules/users/img/buttons/google.png">
</a>
<a href="/auth/linkedin" class="undecorated-link">
<img src="/modules/users/img/buttons/linkedin.png">
</a>
</div>
将现有链接替换为指向您尚未创建的/ auth / meetup路由的链接,并显示尚未下载的Meetup.com图标(如清单3所示)。
清单3.链接到auth / meetup
<h3 class="col-md-12 text-center">Sign up using your Meetup.com account</h3>
<div class="col-md-12 text-center">
<a href="/auth/meetup" class="undecorated-link">
<img src="/modules/users/img/buttons/meetup.png">
</a>
</div>
访问“ Meetup图标”页面,并将128x128像素的图像保存到存储其他社交媒体图标的public / modules / users / img / buttons /。
现在已经禁用了注册页面,请在文本编辑器中打开public / modules / users / views / signin.client.view.html,并以与注册页面相同的方式对其进行调整(如清单1所示)。 4)。
清单4. public / modules / users / views / signin.client.view.html
<h3 class="col-md-12 text-center">Sign in using your Meetup.com account</h3>
<div class="col-md-12 text-center">
<a href="/auth/meetup" class="undecorated-link">
<img src="/modules/users/img/buttons/meetup.png">
</a>
</div>
如果一切都按计划进行,那么新的注册页面将如图2所示。当然,如果没有适当的路线,则单击链接将出现404页面未找到错误。 接下来,您将解决此问题。
图2.新的UGLI注册页面
设置服务器端的身份验证/会议路由
下一步是创建服务器端的auth / meetup路由。 回想一下,所有服务器端逻辑都存储在app目录中; 客户端逻辑存储在公用文件夹中。
在文本编辑器中打开app / routes / users.server.routes.js。 找到Facebook的代码块并复制/粘贴,然后用meetup
替换facebook
(如清单5所示)。
清单5. app / routes / users.server.routes.js
// Setting the facebook oauth routes
app.route('/auth/facebook').get(passport.authenticate('facebook', {
scope: ['email']
}));
app.route('/auth/facebook/callback').get(users.oauthCallback('facebook'));
// Setting the meetup oauth routes
app.route('/auth/meetup').get(passport.authenticate('meetup', {
scope: ['email']
}));
app.route('/auth/meetup/callback').get(users.oauthCallback('meetup'));
还记得上一节中在注册和登录页面上创建的指向auth / meupup的超链接吗? 当用户通过单击链接向服务器发送HTTP GET
请求时,将触发第一条路由(auth / meetup)。 Passport将尝试通过使用passport-meetup
策略来验证用户身份。 登录尝试的结果(成功或否则)将异步发送到第二个auth / meetup / callback路由。
如果现在单击注册页面上的“会议集会”链接,则会收到500服务器错误而不是404错误。这不完全是一种改进,但至少是一种进步。 下一步:配置聚会策略。
配置聚会策略
您可以在同名的config / strategies目录中找到所有Passport策略。 将facebook.js复制到meetup.js,然后在文本编辑器中打开meetup.js。
与上一节中的操作一样,您将遍历此文件并将所有facebook
实例替换为meetup
。 但这不仅仅是简单的查找/替换操作。 您还需要进行一些小的配置更改。
首先,将文件顶部所需的库从Facebook策略更改为Meetup策略(如清单6所示)。
清单6. config / strategies / meetup.js
/**
* Module dependencies.
*/
var passport = require('passport'),
url = require('url'),
MeetupStrategy = require('passport-meetup').Strategy,
config = require('../config'),
users = require('../../app/controllers/users');
接下来,您需要自定义传递给新策略的选项块。 这些价值因策略而异。 清单7显示了Facebook策略选项,这些选项不适用于Meetup。
清单7.不适用于Meetup的Facebook选项
module.exports = function() {
// Use facebook strategy
passport.use(new FacebookStrategy({
clientID: config.facebook.clientID,
clientSecret: config.facebook.clientSecret,
callbackURL: config.facebook.callbackURL,
passReqToCallback: true
},
值得庆幸的是,您npm install
passport-meetup
模块附带了示例代码。 在文本编辑器中打开node_modules / passport-meetup / examples / login / app.js。 查找passport.use function call
(如清单8所示)。
清单8. node_modules / passport-meetup / examples / login / app.js
passport.use(new MeetupStrategy({
consumerKey: MEETUP_KEY,
consumerSecret: MEETUP_SECRET,
callbackURL: "http://127.0.0.1:3000/auth/meetup/callback"
},
将此代码段复制到metup.js,覆盖Facebook代码。 接下来,将冒号右侧的值更改为清单9中所示的值。
清单9.会用的Meetup选项
passport.use(new MeetupStrategy({
consumerKey: config.meetup.consumerKey,
consumerSecret: config.meetup.consumerSecret,
callbackURL: config.meetup.callbackURL,
},
在下一部分中,您将从Meetup.com获取consumerKey
和consumerSecret
,并将它们保存在config.js文件中。 但是,您需要先对当前文件进行一些其他更改,然后再进行操作。
紧随new MeetupStrategy
构造函数之后的函数是事件处理程序,该事件处理程序从Meetup.com接收响应。 您对响应的三个关键部分感兴趣:访问令牌,刷新令牌和用户配置文件。 (有关令牌的详细信息,请参见OAuth访问令牌和刷新令牌侧栏。)
访问令牌和刷新令牌是您不变地传递给Passport的字符串。 尽管它们对于OAuth操作的成功至关重要,但他们很无聊。 (清单10包含了两者的示例。)
用户个人资料更有趣。 这是OAuth提供程序返回的JSON对象,其中包含有关成功通过身份验证的用户的信息。 具体细节因OAuth提供者而异。 清单10显示了Meetup返回的用户个人资料的示例。
清单10.从Meetup.com OAuth提供者返回的用户个人资料
{
provider: 'meetup',
id: 13848777,
displayName: 'Scott Davis',
_raw: '{
"results": [{
"status": "active",
"link": "http:\\\/\\\/www.meetup.com\\\/members\\\/13848777",
"photo": {
"photo_link": "http:\\\/\\\/photos1.meetupstatic.com\\\/photos\\\/member\\\/7\\\/4\\\/d\\\/2\\
\/member_11849906.jpeg",
"thumb_link": "http:\\\/\\\/photos3.meetupstatic.com\\\/photos\\\/member\\\/7\\\/4\\\/d\\\/2\\
\/thumb_11849906.jpeg",
"photo_id": 11849906
},
"country": "us",
"state": "CO",
"city": "Denver",
"id": 13848777,
"joined": 1295844957000,
"bio": "Scott Davis is the founder of ThirstyHead.com, a training and
consulting company that specializes in leading-edge technology solutions like
HTML 5, NoSQL, Groovy, and Grails.",
"name": "Scott Davis",
"other_services": {
"twitter": {
"identifier": "@scottdavis99"
}
}
}]
}',
_json: {
results: [ [Object] ],
meta: {
link: 'https://api.meetup.com/2/members',
total_count: 1,
url: 'https://api.meetup.com/2/members?order=name&member_id=13848777&offset=0
&format=json&page=800',
title: 'Meetup Members v2',
updated: 1392763702000,
description: 'API method for accessing members of Meetup Groups',
method: 'Members',
},
accessToken: 'c7b5577bb80aab55439785cd86abcdef',
refreshToken: '2af98db68950235a1e2519a734abcdef'
}
}
如您所见,Meetup返回用户详细信息,例如姓名,居住地,加入日期,个人资料照片,链接的社交媒体帐户等。
完成Meetup策略的自定义操作的最后一件事是将Meetup配置文件字段映射回app / models / user.server.model.js中定义的User Mongoose对象。 在config / strategies / meetup.js中编辑其余的Facebook块,如清单11所示。
清单11.将OAuth用户配置文件映射到User
对象
// Create the user OAuth profile
var providerUserProfile = {
firstName: '',
lastName: '',
displayName: profile.displayName,
email: '',
username: profile.id,
provider: profile.provider,
providerIdentifierField: 'id',
providerData: providerData
};
如果您在Meetup配置文件JSON中看到要添加到User
对象的字段,那么这是进行更改的最佳时机。 不要忘记在public / modules / users / views中将新字段添加到HTML表单中。
完成的config / strategies / meetup.js应该类似于清单12。
清单12.完整的config / strategies / meetup.js
'use strict';
/**
* Module dependencies.
*/
var passport = require('passport'),
url = require('url'),
MeetupStrategy = require('passport-meetup').Strategy,
config = require('../config'),
users = require('../../app/controllers/users');
module.exports = function() {
// Use meetup strategy
passport.use(new MeetupStrategy({
consumerKey: config.meetup.clientID,
consumerSecret: config.meetup.clientSecret,
callbackURL: config.meetup.callbackURL,
},
function(req, accessToken, refreshToken, profile, done) {
// Set the provider data and include tokens
var providerData = profile._json;
providerData.accessToken = accessToken;
providerData.refreshToken = refreshToken;
// Create the user OAuth profile
var providerUserProfile = {
firstName: '',
lastName: '',
displayName: profile.displayName,
email: '',
username: profile.id,
provider: profile.provider,
providerIdentifierField: 'id',
providerData: providerData
};
// Save the user OAuth profile
users.saveOAuthUserProfile(req, providerUserProfile, done);
}
));
};
在测试此代码之前,您还需要做一件事:从Meetup获取consumerKey
和consumerSecret
。
获取Meetup的consumerKey
和consumerSecret
到目前为止,我已经用了整整篇文章来讨论对用户进行身份验证。 但是,在用户可以使用OAuth登录UGLI之前,您(开发人员)必须提供证明您的组织符合要求的证据。 您可以通过向用户提供公钥( consumerKey
)来实现。 您的应用程序还需要知道其私钥( consumerSecret
)是什么。
如果您以前曾使用过公钥基础结构 (PKI),那么您将知道隐藏私钥并确保其安全至关重要。 如果其他人发现了您的私钥,他们可以伪装成您的组织。 相反,如果您不与用户共享您的公钥,那么他们将无法证明您是谁。
如果您是Meetup.com上用户组的组织者,则可以从Meetup的“ 您的OAuth消费者”页面中生成consumerKey
和consumerSecret
(请参见图3)。 我将HTML5 Denver User Group
用作消费者名称,将http://www.meetup.com/HTML5-Denver-Users-Group/
用于应用程序网站,并将http://localhost:3000/auth/meetup/callback
用于重定向URI。 将UGLI应用程序投入生产后,我将把应用程序网站更改为http://html5denver.com
,并将重定向URI更改为http://html5denver.com/auth/meetup/callback
。
图3.生成HTML5 Denver的consumerKey
和consumerSecret
如果您没有在Meetup.com上运行用户组,则您的帐户无权代表该组生成OAuth密钥。 但是,您仍然可以为其他社交媒体帐户之一生成令牌,并相应地调整本文中的步骤。 请参阅实施通过Twitter登录以为您的Twitter帐户生成应用程序密钥,或访问访问令牌以使用您的Facebook帐户。 在your social media website oauth keys
上进行快速网络搜索应该会得到逐步说明。
将组织OAuth密钥添加到您的应用程序
获得两个密钥(公共密钥和私有密钥)之后,您将通过环境变量将它们添加到您的应用程序中-就像在“ 浏览MEAN应用程序 ”中更改PORT
。
回想一下,您可以设置变量,这些变量根据您所运行的模式( development
, production
或test
。 特定于环境的值存储在config / env中。 在文本编辑器中打开config / env / development.js。 复制/粘贴Facebook块,并针对Meetup进行相应调整(如清单13所示)。 可以肯定的属性名称匹配这里您在使用的属性名称passport.use
在配置/策略/ meetup.js函数调用。
清单13. config / env / development.js
'use strict';
module.exports = {
db: 'mongodb://localhost/test-dev',
app: {
title: 'HTML5 Denver'
},
meetup: {
consumerKey: process.env.MEETUP_KEY || 'APP_ID',
consumerSecret: process.env.MEETUP_SECRET || 'APP_SECRET',
callbackURL: 'http://localhost:3000/auth/meetup/callback'
},
facebook: {
clientID: process.env.FACEBOOK_ID || 'APP_ID',
clientSecret: process.env.FACEBOOK_SECRET || 'APP_SECRET',
callbackURL: 'http://localhost:3000/auth/facebook/callback'
},
twitter: {
clientID: process.env.TWITTER_KEY || 'CONSUMER_KEY',
clientSecret: process.env.TWITTER_SECRET || 'CONSUMER_SECRET',
callbackURL: 'http://localhost:3000/auth/twitter/callback'
},
google: {
clientID: process.env.GOOGLE_ID || 'APP_ID',
clientSecret: process.env.GOOGLE_SECRET || 'APP_SECRET',
callbackURL: 'http://localhost:3000/auth/google/callback'
},
linkedin: {
clientID: process.env.LINKEDIN_ID || 'APP_ID',
clientSecret: process.env.LINKEDIN_SECRET || 'APP_SECRET',
callbackURL: 'http://localhost:3000/auth/linkedin/callback'
}
};
您可以使用上一部分中检索到的consumerKey
和consumerSecret
硬编码值替换APP_ID
和APP_SECRET
。 但是更安全的解决方案是通过环境变量将这些值提供给UGLI应用程序。 要使用组织的consumerKey
和consumerSecret
启动应用程序,请输入:
MEETUP_KEY=l75fkklhurkack36eelfhhfhjc MEETUP_SECRET=abcdeg316jd3ni43f21u1abcde NODE_ENV=development grunt
上线之前,请不要忘记对config / env / production.js进行类似的调整。 而且,如果您之前创建了用户帐户,请确保将其从MongoDB的html5-denver-dev数据库中删除,以便您可以重新完成新帐户的创建过程。
结论
Perl编程语言的创建者拉里·沃尔(Larry Wall)曾有句著名的话:“简单的事情应该很容易,而困难的事情应该可以。” 我希望这一观点能很好地总结您将OAuth和Passport连接起来以使用Meetup.com满足您的分布式身份验证和授权需求的经验。
在下一期Mastering MEAN文章中 ,我将带您了解MEAN堆栈中内置的测试基础结构。 您将了解用于服务器端测试的Mocha,用于客户端测试的Jasmine以及用于在多个浏览器上运行测试的Karma。 在此之前,请尽情掌握MEAN。
翻译自: https://www.ibm.com/developerworks/web/library/wa-mean5/index.html
oauth身份验证方式