第一部分 起步
主要介绍:
- 什么是 OAuth2.0, 以及其工作原理.
- 在此之前如何处理授权问题.
- 介绍 OAuth 的适用范围.
该部分是理论基础, 与技术痛点.
ch01 OAuth2.0 是什么, 为什么要关心它
主要内容:
- OAuth2.0 是什么
- 不用 OAuth, 怎么做
- OAuth 的原理
- OAuth2.0 不能做什么
首先 OAuth 是一个安全协议,
其次, 它使用很广泛, 而且很重要.
1.1 OAuth2.0 是什么
OAuth2.0 是一个授权协议. 它允许软件代表资源拥有者去访问资源拥有者的资源.
注意这里是代表, 不是充当.
应用向资源拥有者请求授权, 然后获得令牌 (token), 然后使用令牌来访问资源.
其中访问权限, 能力等由令牌所限制.
作者使用了一个案例场景:
你有一个照片云存储服务和一个云打印服务. 并希望使用云打印服务来打印云存储中的照片. 但这两个服务部署于一个服务商. 即两方不同账户.
该场景围绕后续的示例. OAuth 似乎就是为了如今 WebAPI 应用所诞生的.
作者还描述, 一般使用 OAuth 是很丝滑的, 一般不会觉得突兀, 甚至不被察觉. 作者建议读者在阅读后可以看看身边哪些地方使用了 OAuth.
书中引用了 OAuth 协议规范:
OAuth 2.0 框架能让第三方应用以有限的权限访问 HTTP 服务,可以通过构建资源拥有者与 HTTP 服务间的许可交互机制,让第三方应用代表资源拥有者访问服务,或者通过授予权限给第三方应用,让其代表自己访问服务。
OAuth 是授权框架, 其目的是让一个系统组件获取另一个系统组件的访问权限.
一个典型的应用是: 客户端应用, 代表资源拥有者 访问受保护资源.
这里有几个术语:
- 资源拥有者. 实际权限所有者.
- 受保护资源. 需要访问的目标. 可以是具体资源, 也可以是 API.
- 客户端. 无权限的应用, 或第三方通用应用.
模型图:
然后作者将其与云打印的案例进行对应解释.
1.2 黑暗的旧时代: 凭据共享与凭据盗用
作者介绍了在 OAuth 之前的实现方式, 并介绍了其弊端.
常规的做法:
- 复制用户的凭据并用它登录另一个服务.
- 简单, 但不安全.
- 实际上就是客户端代表了资源拥有者.
- 由于是应用充当用户, 无法限制访问权限, 无法控制颗粒度.
- 给客户端颁发一个开发者秘钥.
- 开发者秘钥是全局秘钥, 可用该秘钥访问所有用户. 不安全.
- 好处是不用暴露用户秘钥, 但前提是受保护资源要信任客户端, 对于多服务商几乎无法实现.
- 给用户提供特殊密码, 该密码仅提供给第三方服务. 缺点是不易维护.
上述的三种方案都是在没有 OAuth 之前的常规实现方式. 或兼容方案.
1.3 授权访问
OAuth 引入了授权服务器. 目的是通过 OAuth 协议, 资源拥有者将受保护资源部分权限委托给客户端应用. 模型逻辑图:
然后作者简要描述了在四个对象之前的请求流程 (简要流程).
- 可以将资源拥有者理解为操作人.
- 客户端理解为浏览器.
- 浏览器需要请求的是受保护资源.
在这个过程中, 没有将秘钥透露给客户端. 认证过程通常使用重定向来实现, 中间的秘钥不会在中间被处理或保存.
也没有强大的开发者秘钥, 不存在对资源无限制的访问.
令牌的请求过程, 用户不必进行查看和维护 (复制粘贴等).
上述是对传统处理办法的对比, 描述了 OAuth 的优势. 但 OAuth 的细节并未加以介绍.
1.3.1 超越 HTTP 基本认证协议和密码共享反模式
上述 "传统" 方法都是密码反模式的案例 (存在安全隐患).
这个安全问题与 MD5 的安全隐患类似. 因为大多数人的多个不同领域的密码大多会相同.
然后介绍了 HTTP API 的密码保护方式. 即 HTTP 基本认证协议, 或 HTTP 摘要认证. 即: 用户提交用户名与密码. 然后利用会话机制, 保证无状态的 HTTP 可以每次都带上用户凭证.
逻辑上就是在描述传统的认证方式 (session + cookie 机制).
由于传统机制多由浏览器自动完成, 这个模型也沿用到 API 中.
OAuth 从一开始就是为 API 协议所定制的.
实际上本节的描述算是对历史, 或一些背景知识的描述.
1.3.2 授权委托: 重要性及应用
委托是 OAuth 中的一个核心概念. 虽然 OAuth 常被称为授权协议, 但它也是一个委托协议. 通常委托的是用户权限的子集.
委托与授权存在较为精细的区别, 中文中似乎不太注重区分.
OAuth 本身不承载权限, 只是提供一种方法, 让客户端来请求用户的部分权限.
然后作者又将这个模型与云打印的服务进行对比解释.
然后描述了授权协议与委托协议. 但不依旧不太明白其区别. 但其中有几点:
- OAuth 令牌中的信息对大多数组件不透明.
- 只有受保护资源理解该令牌的信息.
- 授权信息不一定存在在令牌中. 可以从令牌中获取, 也可以通过其他服务获取.
1.3.3 用户主导的安全与用户的选择
主要描述了安全决策由集权机构负责. 但 OAuth 将其交给了用户手中. 同时描述了 OAuth 遵循 TOFU (首次使用时信任, trust on first use) 原则.
1.4 OAuth 2.0: 优点, 缺点和丑陋的方面
OAuth 是由多个可移动的组件构成的协议. 其设计有一个重要假设, 不受控的客户端总是比授权服务器或者受保护资源多出好几个数量级. 那么, 形成一个模型:
- 一台授权服务器
- 多个需要授权的客户端
那么就需要将复杂性尽可能放在授权服务器中. 那么客户端就变得简单了.
OAuth 令牌相比密码更为复杂, 但是安全性更高.
另一方面, 授权服务器和受保护资源将承担更多的复杂性和安全性方面的责任.
客户端只需要保护自己的凭据和令牌, 客户端数据的泄露仅对该客户端有影响.
而授权服务器需要维护所有客户端的用户凭据和令牌.
OAuth 2.0 的可扩展性和模块化是其最大的优势之一.
糟糕的是, OAuth 常被错误的使用, 得到可接受的结果, 如此存在安全隐患.
总结来说, 其强大, 安全, 但是不易使用, 需要充分了解协议本身与模型本身. 从实践上来说, 大多数使用存在不严谨的操作, 导致存在安全隐患.
1.5 OAuth2.0 不能做什么
暂略
1.6 小结
OAuth 是一个安全标准, 它提供了一种安全访问受保护资源的方式, 特别适用于 WebAPI.
- OAuth 关注的是如何获取与使用令牌.
- OAuth 是一个委托协议, 提供跨系统授权方案.
- OAuth 使用委托协议替代了密码共享反模式. 其安全性更高, 可用性更好.
- OAuth 专注与解决小问题集合.
ch02 OAuth 之舞
主要内容
- OAuth2.0 协议概述
- OAuth2.0 系统中的不同组件
- 不同组件如何交互通信
- 不同组件交互内容是什么
本章的主要内容是搭建完整的 OAuth 模型, 并介绍了其交互流程与时序.
2.1 OAuth2.0 协议概览: 获取和使用令牌
OAuth2.0 是一个复杂的安全协议, 需要在不同组件之间进行相互通信.
本质上, OAuth 事务中就两个重要步骤: 颁发令牌, 使用令牌.
一个规范的 OAuth 事务包含下面事件:
- 资源拥有者向客户端表示他希望客户端代表他执行一些任务 (例如下载xxx, 打印xxx).
- 客户端在授权服务器上向资源拥有者请求授权.
- 资源拥有者许可客户端的授权请求.
- 客户端接收到来自授权服务器的令牌.
- 客户端向受保护资源出示令牌.
具体实现细节可能略有不同.
2.2 OAuth2.0 授权许可的完整过程(授权码许可类型)
接下来会展示详细请求流程.
实际上我认为这里可以将四个角色先介绍一下, 再搭建模型.
首先明确 4 个对象:
- 资源所有者. 可以理解为人. (但可以不是)
- 受保护资源. 它可以是数据, 也可以是另一个 API 等.
- 客户端. 可以简单理解为一个完整的前后分离的程序. (可比作微信小程序 + 自己的服务器)
- 授权服务器. 用于认证, 颁发令牌的服务器 (可以理解为微信的服务器)
模型:
- 资源拥有者, 通过客户端, 访问受保护资源.
- 借助授权服务器进行身份认证, 颁发令牌, 如果令牌过期, 可以刷新令牌 (需配置).
- 然后客户端使用令牌访问受保护资源.
- 资源向授权服务器请求验证令牌真伪, 真, 则返回给客户端资源.
存在前提:
- 客户端需要在授权服务器上进行注册 (不是所有程序都可以向授权服务器申请授权).
- 受保护资源自己需要对资源进行权限分类, 或功能分类. 并交给客户端来申请.
- 下面的模型有一个预设:
- 使用 Web 应用作为模型
- 授权服务器地址: http://localhost:9001
- 客户端服务器: http://localhost:9000
- 受保护资源服务器: http://localhost:9002
简化的理想操作模型
用户发起请求 (通过浏览器), 浏览器向自己所在的服务器 (客户端应用) 发送请求所要资源. 服务器 (客户端), 获得授权 (黑盒), 然后向受保护资源发送请求, 所要资源. 受保护资源校验后 (黑盒), 返回资源数据. 服务器 (客户端), 将资源数据响应给浏览器 (用户).
细节模型 [要需要校审微调]
细节模型分为四个阶段
- 重定向到认证服务器
- 身份认证, 生成授权码
code
- 客户端请求认证, 获取
token
- 客户端利用
token
请求受保护资源
请求示例:
第一次发出请求, 客户端无 token, 返回 302, 将浏览器重定向到授权服务器的认证端点
HTTP/1.1 302 Moved Temporarily x-powered-by: Express Location: http://localhost:9001/authorize?response_type=code&scope=foo&client_id=oauth-client-1&redirect_uri=http%3A%2F%2Flocalhost%3A9000%2Fcallback&state=Lwt50DDQKUB8U7jtfLQCVGDL9cnmwHH1 Vary: Accept Content-Type: text/html; charset=utf-8 Content-Length: 444 Date: Fri, 31 Jul 2015 20:50:19 GMT Connection: keep-alive
- 其中
state
为客户端生成的随机码, 用于授权服务器在进行身份认证后, 重新重定向到客户端后, 由客户端来验证该请求是否由自己发出. response_type=code
表示请求为授权码验证模式.scope=foo
用于表示申请的权限范围. 该权限范围是受保护资源事先约定好的鉴权范围.client_id
用于表示所绑定的客户端. 在授权服务器内部进行校验用.redirect_uri
用于描述授权服务器在完成身份认证后生成授权码如何返回给客户端. 授权服务器生成授权码 (code
) 后, 向浏览器返回 302 重定向, 利用重定向的 url 参数, 将授权码以 GET 请求的查询参数的形式返回给客户端. 同时该参数也起到校验的作用. 实际上系统在运行之前, 客户端需要在授权服务器上注册, 也需要注册需要重定向的地址, 因此该参数也可以作为客户端的校验参数.
- 其中
浏览器获得 302 响应, 直接发送 GET 请求进行重定向
GET /authorize?response_type=code&scope=foo&client_id=oauth-client-1&redirect_uri=http%3A%2F%2Flocalhost%3A9000%2Fcallback&state=Lwt50DDQKUB8U7jtfLQCVGDL9cnmwHH1 HTTP/1.1 Host: localhost:9001 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:39.0) Gecko/20100101 Firefox/39.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Referer: http://localhost:9000/ Connection: keep-alive
- 该请求将浏览器重定向到授权服务器的身份认证页面. 该过程完全使用浏览器的重定向功能实现, 不经过客户端. 屏蔽了用户泄露密码登可能.
- 整个认证过程存在一定的 HTTP 请求, 但所有交互均发生在浏览器与授权服务器之间.
认证结束后, 授权服务器生成一次性授权码 (
code
), 然后返回 302, 重定向回客户端. 然客户端向授权服务器发送请求, 请求访问令牌 (access_token
).HTTP 302 Found Location: http://localhost:9000/callback?code=8V1pr0rJ&state=Lwt50DDQKUB8U7jtfLQCVGDL9cnmwHH1
注意该重定向是叫浏览器重定向到客户端 (从授权服务器切换至客户端). 另外该重定向中带有授权码
code
.因此浏览器会发起新的 GET 请求, 直接进入客户端.
GET /callback?code=8V1pr0rJ&state=Lwt50DDQKUB8U7jtfLQCVGDL9cnmwHH1 HTTP/1.1 Host: localhost:9000
客户端验证
state
后, 表示为自己发出的认证请求 (避免被攻击, 暴力破解授权码, 或浪费服务器资源), 然后向授权服务器的token
端点发送POST
请求, 申请访问令牌:POST /token Host: localhost:9001 Accept: application/json Content-type: application/x-www-form-encoded Authorization: Basic b2F1dGgtY2xpZW50LTE6b2F1dGgtY2xpZW50LXNlY3JldC0x grant_type=authorization_code&redirect_uri=http%3A%2F%2Flocalhost%3A9000%2Fcallback&code=8V1pr0rJ
- 其中请求头中
Authorization: Basic XXX
为客户端的 ID 和秘钥, 使用冒号分隔后采用 Base64 格式序列化. 用于在授权服务器对客户端进行校验. - 请求体中
grant_type=authorization_code
表示授权方式: 授权码方式, 用于在授权服务器中确定授权流程. - 请求体中的
code
为一次性授权码. - 请求体中的
redirect_uri
表示重定向的地址. 该处实际上不会再进行重定向. 而是 OAuth 规范中约定, 授权请求中提供了重定向, 令牌请求中也需要提供重定向, 目的是为了进行校验, 避免篡改 URL 来盗用信息.
- 其中请求头中
认证服务器获得授权码后, 进行验证. 该授权码为一次性使用. 验证通过后, 生成随机的访问令牌 (
access token
), 然后返回给客户端.HTTP 200 OK Date: Fri, 31 Jul 2015 21:19:03 GMT Content-type: application/json { "access_token": "987tghjkiu6trfghjuytrghj", "token_type": "Bearer" }
2.3 OAuth 中的角色: 客户端, 授权服务器, 资源拥有者, 受保护资源
分别解释了这四个对象的作用与实际中是什么.
具体在上一节中均有描述, 细节略.
2.4 OAuth 的组件: 令牌, 权限范围和授权许可
除了客户端, 授权服务器, 资源拥有者, 以及受保护资源外, OAuth 生态系统还依赖于其他几种机制.
- 访问令牌. OAuth 访问令牌, 简称令牌. 由授权服务器办法给客户端. OAuth 协议没有约定其形式. 但其用于描述客户端所具备的权限. 但令牌对客户端是不透明的. 授权服务器, 和受保护资源因为需要颁发与验证, 它们是需要理解令牌的.
- 权限范围. OAuth 权限范围, 用于描述访问受保护资源的权限有哪些. 约定使用字符串描述, 用空格分隔. OAuth 协议没有定义权限范围的格式. 权限范围由受保护资源定义, 可以存在交叉 (逻辑上和组 group 的概念一样). 授权服务器允许资源所有者来筛选使用. 逻辑上这个是需要预定义的.
- 刷新令牌. OAuth 刷新令牌与令牌逻辑是类似的, 但刷新令牌不会发给受保护资源. 客户端使用刷新令牌的目的是为了更新令牌. 另外作为扩展功能, 刷新令牌可以缩小令牌权限范围.
- 授权许可. 是 OAuth 协议中权限获取方法. 是获取令牌的方式.
刷新令牌模型:
- 客户端访问资源, 资源服务器校验令牌, 过期, 或失效, 或丢失, 返回鉴权失效.
- 客户端接收到令牌失效的响应, 再次向授权服务器请求刷新令牌.
- 授权服务器同时更新令牌和刷新令牌, 返回给客户端.
- 客户端存储令牌与刷新令牌, 再次请求资源.
2.5 OAuth 的角色与组件间的交互: 后端信道, 前端信道和端点
主要是这几个术语:
- 后端信道, 单纯 HTTP 请求通信的模型, 不涉及浏览器.
- 前端信道, 浏览器中, 基于重定向机制完成的通信模型, 与客户端隔离.
其他略.