原文地址:http://www.moye.me/?p=592

OAuth是什么

OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。

OAuth 2.0  

OAuth的版本有v1.0, v1.0a 和 v2.0。OAuth 2.0 的出现主要是解决1.0+中的几个问题,提升开发简易度和应用安全性:

  • 更好的支持非浏览器APP(移动和桌面客户端,可以省略v1.0的交换token过程,增强用户体验)
  • 给访问令牌(Access-Token)添加了续期概念,对令牌泄露多了一层防御
  • 不再强制客户端使用Token Secret,有令牌就够了
  • 引入Bearer 验证机制

OAuth 2 是目前被广泛支持的版本,但不向下兼容1.0。

术语

简单说,OAuth 2涉及到三方

  1. Client APP:要访问用户的程序
  2. Resource Owner :用户,由他来给APP授权
  3. Authorization Server:也称为 API Provider(Google/Facebook/Twitter…)
    1. client_id 和 client_secret:Server颁发给 APP 的身份凭证
    2. Access Token:Server颁发的令牌,访问资源API需要带着它
    3. Scope:APP 向Server 提供需要调用的API种类
    4. Redirect URL:APP 向 Server提供的回调地址

OAuth 2 交互模型

OAuth 2的 交互模型比较灵活,主要的交互模型分为如下几种:

  • 服务端Authorization code授权:被 Web服务端编程广泛采用,也是本文重点介绍的模型
  • 客户端隐式授权:Web客户端编程(JavaScript APP)使用这种方式交互,在用户授权后直接取到令牌
  • Resource Owner登录授权:需要用户输入用户名和密码来交换令牌
  • 客户端凭证:这种模型下,APP 可能就是Owner,代表自己进行API调用

准备工作

首先,开发人员需要去API Provider处,为APP 创建应用信息,申请权限,以Google为例:

  1. 在 Google Developers Console 创建项目
  2. 在 APIs & Auth -> APIs 中,选择打开需要调用的API
  3. 在 APIs & Auth -> Credentials 中,填写 Redirect URIS(OAuth 2 回调的地址),创建Client ID。成功后,会得到需要的 CLIENT ID 和 CLIENT SECRET,保存好不要泄露 
  4. 在 APIs & Auth -> Consent screen 中,填写 Email/ Product Name/ Homepage/ Logo等 APP元信息,这些是给用户看的,在交互过程中会出现在确认授权页

服务端交互流程

OAuth 2 的服务端交互流程,是一个对用户透明的三方过程:

如果用Node.js 实现前述的 Google API OAuth 2访问,编程模型大概如此:

  1. 判断是否已有 access_token,过期了吗?如果不存在或过期,一步步来:
  2. 将用户页面跳向到https://accounts.google.com/o/oauth2/auth(附上一系列Query参数:response_type/ client_id/ redirect_uri/ scope,视需要追加参数:access_type/ approval_prompt/ state…)
  3. 为 redirect_uri 提供 HTTP GET 方法的处理,以响应回调
  4. 第3步的回调会收到一个code参数,用它向 https://accounts.google.com/o/oauth2/token 发起一个 POST请求,这次需要提供的参数:code / client_id/ client_secret/ redirect_uri/ grant_type
  5. 为 第4步 的redirect_uri 提供HTTP GET 方法的处理,以响应回调
  6. 第5步的回调会收到一个 JSON对象,里面有access_token 和它的过期时间expires_in,将它存下来
  7. 访问API,附上得到的 access_token

引入Passport

显然,自己实现OAuth 流程将需要写不少东西,且每增加一个API Provider,这个过程需要再来一次。这行里有句黑话:写得越多,错得就越多   这时候,应该找个合适的框架

Passport 框架就是为解决类似问题而生的:它以中间件的形式为Node 程序提供身份认证,框架本身将一般形式的认证过程(Basic & Digest/ OAuth/ Open ID)、回调及错误处理进行了封装,而将具体的认证实现抽象为Strategy(策略),与框架本身并无关系,只要是符合Passport 的Strategy都能以插件的形式加入项目被Passport使用。比如基于Google的OAuth 2认证,我们可以用 passport-google-oauth,基于Facebook的OAuth 2认证我们可以用passport-facebook,当然也可以用别的或者自己写。 这种基于策略的抽象大大简化了编程模型,所以Passport 有自信称 " Simple, unobtrusive authentication for Node.js",诚不欺我。

Passport 实现 Google 用户登录

Google 作为具体的 API Provider,用Passport 对其进行OAuth 2访问,需要一个 Strategy 来提供它的交互流程实现,本例中使用 passport-google-oauth

在package.json中确定引用,并用 npm install 安装模块:

  1. "dependencies": {
  2. //... more libraries
  3. "passport": "*",
  4. "passport-google-oauth": "*",
  5. }
 将从 Google Developers Console 获得的client_id 和 client_secret 存到配置文件中:

  1. {
  2. //...more configuration
  3. "GOOGLE_CLIENT_ID" : "xxxx.apps.googleusercontent.com",
  4. "GOOGLE_CLIENT_SECRET" : "wdsfas3_-safdsafasf",
  5. "GOOGLE_RETURN_URL" : "http://www.xxx.com/auth/google/return",
  6. }

上篇提到的配置读取器configUtils将能自动读取到它,结合这些实现Google OAuth 2认证(authUtils.js:

  1. var passport = require('passport')
  2. , GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
  3.  
  4. var config = require('../configUtils');
  5.  
  6. passport.use(new GoogleStrategy({
  7. authorizationURL: 'https://accounts.google.com/o/oauth2/auth',
  8. tokenURL: 'https://accounts.google.com/o/oauth2/token',
  9. clientID: config.getConfigs().GOOGLE_CLIENT_ID,
  10. clientSecret: config.getConfigs().GOOGLE_CLIENT_SECRET,
  11. callbackURL: config.getConfigs().GOOGLE_RETURN_URL
  12. },
  13. function (accessToken, refreshToken, profile, done) {
  14. var userInfo = {
  15. 'type': 'google',
  16. 'userid': profile.id,
  17. 'name': profile.displayName,
  18. 'email': profile.emails[0].value,
  19. 'avatar': profile._json.picture
  20. };
  21. return done(null, userInfo);
  22. }
  23.  
  24. passport.serializeUser(function (user, done) {
  25. done(null, user);
  26. });
  27.  
  28. passport.deserializeUser(function (obj, done) {
  29. done(null, obj);
  30. });
  31.  
  32. ));

代码中的 function (accessToken, refreshToken, profile, done) 就是用户授权后的回调,Passport 会将获取到的用户信息包装成profile,里面的转换代码可以自由发挥,最后记得调用done,并将用户信息返回。done 就是我们定义在路由里的回调,稍后会提到。

serializeUser 和 deserializeUser 是转换用的序列化/ 反序列化。

结合Express

在  authUtils.js 中导出给 Express 用的路由:

  1. module.exports = function (app) {
  2. app.use(passport.initialize());
  3.  
  4. app.get('/auth/google',
  5. passport.authenticate('google', {
  6. scope: 'https://www.google.com/m8/feeds https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile'
  7. }));
  8.  
  9. app.get('/auth/google/return',
  10. passport.authenticate('google', { failureRedirect: '/login' }),
  11. function (req, res) {
  12. if (req.user) {
  13. //...some user login handling code
  14. }
  15. res.redirect('/');
  16. });
  17. };

第1个 get 路由是我们可以在UI 上引用的地址,点击它开始OAuth 2 流程,即 Passport 的入口;第2个 get路由是Passport 的出口,即上节提到被 done 方法回调的地址,此时整个 OAuth 2 流程完成,程序得到了 user 信息(Passport将它注入到了 req上,但仅此一次可用,阅后即焚)。在认证过程发生任何错误,将跳转到  failureRedirect 指定的地址。Passport为我们做了诸如 code 交换 token的一系列琐碎事情。

app.js 中调用,So easy:

  1. var authUtils = require('../authUtils');
  2. authUtils(app);

小结

OAuth 2 协议为用户资源的授权提供了一个安全的、开放而又简易的标准,但即便如此,在Node.js 的 OAuth 2交互实现中,仍需 为Owner/App/Server的三方交互流程编写很多代码。使用Passport框架,将大大简化这一编程模型,使我们可以将更多精力投入到业务实现上。

本文提供的仅是一个基于Passport框架的OAuth 2方案思路,代码部分也仅是个骨架,望抛砖引玉。

更多文章请移步我的blog新地址: http://www.moye.me/

[Node.js] OAuth 2 和 passport框架的更多相关文章

  1. Pomelo:网易开源基于 Node.js 的游戏服务端框架

    Pomelo:网易开源基于 Node.js 的游戏服务端框架 https://github.com/NetEase/pomelo/wiki/Home-in-Chinese

  2. node.js之十大Web框架

    之前接触过Node.js是因为好奇大前端越来越能干了,连我后台的饭碗都要抢了,太嚣张了,于是我想打压打压它,然后就这样接触它了.再到后来是因为Settings-Sync插件二次开发,我需要用node. ...

  3. 最受欢迎的5款Node.js端到端测试框架

    测试,尤其是自动化测试在现代 WEB 工程中有着非常重要的角色,与交付过程集成良好的自动化测试流程可以在新版发布时帮你快速回归产品功能,也可以充当产品文档.测试因粒度不同又可以分为单元测试.接口测试. ...

  4. node.js安装express模块应用服务框架

    1.创建工程文件夹case-04 2.在终端窗口进入文件夹目录,并输入:npm init,并一路回车,最后看到在case-04文件夹里自动生成了package.json 文件 3.打开vscode,进 ...

  5. Node.js 模块之【passport】

    什么是passport passport是Nodejs的一个中间键,用于用户名和密码的验证登陆.在项目中我用它来验证后台用户名和密码,但passport更多用在第三方登录,功能强大. 安装与配置 本项 ...

  6. 全端开发必备!10个最好的 Node.js MVC 框架

      Node.js 是最流行的 JavaScript 服务端平台,它允许建立可扩展的 Web 应用程序.Node.js 包含不同类型的框架,如 MVC 框架.全栈框架.REST API  以及大量的服 ...

  7. Node.js Web框架收集

    原文地址:http://geek.csdn.net/news/detail/4020 框架列表: http://nodeframework.com/ 与其他很多语言一样,Node.js也有很多Web框 ...

  8. hexo —— 简单、快速、强大的Node.js静态博客框架

    hexo是一款基于Node.js的静态博客框架.目前在GitHub上已有1375 star 和 219 fork. 特性 风一般的速度 Hexo基于Node.js,支持多进程,几百篇文章也可以秒生成. ...

  9. Express 4.x Node.js的Web框架

    为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/SJQ. http://www.cnblogs.com/shijiaqi1066/p/3821150.html ...

随机推荐

  1. android: PendingIntent的使用

    PendingIntent的Flags的类型: * Flags的类型: FLAG_ONE_SHOT:得到的pi只能使用一次,第二次使用该pi时报错 FLAG_NO_CREATE: 当pi不存在时,不创 ...

  2. 采用TL026等构成的宽带ALC放大器电路图

    Building a Differential Amplifier An op-amp with no feedback is already a differential amplifier, am ...

  3. C# 动态修改dll的签名 以及修改引用该dll文件的签名

    在读取RedisSessionStateProvider配置 提到用mono ceil 来修改程序集以及它的签名,里面GetPublicKey 和GetPubliKeyToken 方法里面那个字符串的 ...

  4. atmega32u4制作arduino leonardo最小系统

    转载请注明:@小五义http://www.cnblogs.com/xiaowuyiQQ群:64770604 一.leonardo最小系统 关于leonardo这里不再介绍,直接上最小系统原理图,该系统 ...

  5. 向Spotify学习如何设计产品(转)

    导语:Spotify是瑞典的精益创业项目,同时保持着很棒的产品交付记录.一般在产品上线之后,开发者才知道人们喜不喜欢它.Spotify如何解决这个问题? 本文转自 kent.zhu's Blog,原文 ...

  6. Hbase&Hadoop常用命令

    Hbase中根据Rowkey的前缀Prefix查询数据: scan 'test_xiaomifeng_monitoring_log',{FILTER => "(PrefixFilter ...

  7. netsh-winsock-reset;ping的通公网IP和DNS地址和内网网关,就是不能解析域名;

    winXP cmd-------------> netsh winsock reset ============= 相关知识: netsh winsock reset命令含义是重置 Winsoc ...

  8. 在Android Studio进行“简单配置”单元测试(Android Junit)

    起因 在Android studio 刚出.本人就想弄单元测试,可惜当时Android studio不知道抽什么风(准确来说,应该是我不会弄而已).无法执行到相应的代码.后来今天突然自己又抽风.又想去 ...

  9. 在线制作h5——上帝的礼物

    在线制作h5 网址:http://www.godgiftgame.com 网站名称:上帝的礼物 推荐指数:5颗星 功能概要 可以设置背景.元素图片.元素文字.元素图形.声音.加载.链接.分享,生成h5 ...

  10. 使用python pylab库 画线

    pylab 提供了比较强大的画图功能,但是函数和参数都比较多,很容易搞混.我们平常使用最多的应该是画线了.下面,简单的对一些常用的划线函数进行了封装,方便使用. # -*- coding: utf-8 ...