本文参考文章:

什么是 OAuth 2.0 ?为什么我们需要它?

OAuth 2.0 是一个开放标准,允许用户授权第三方应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方应用。

一个最简单的例子就是支付宝的免密支付。例如我在订阅微博会员按月自动续费服务时,需要获得支付宝授权,因为本质上这个操作是用户在 A 服务(微博)上授权 B 服务(支付宝)的数据,而 A B 服务之间并没有直接的信任关系。

那么问题来了,在支付宝的研发侧视角看,我应该怎么确保这个微博用户真的请求了他自己的支付宝账户数据?微博对我而言是一个不可信的第三方,我怎么知道它没有伪造请求?这需要我们有一种机制来确保用户的授权请求是合法的,这也就是 OAuth 2.0 的主要作用。

OAuth 2.0 架构

摘自 RFC 6749:

OAuth 2.0 基本架构

资源映射,以上文支付宝授权为例:

  • 资源拥有者:C 端用户
  • 客户端:微博
  • 认证服务器:支付宝认证服务器/第三方可信认证服务器
  • 资源服务器:支付宝服务接口
  • 资源:支付宝用户信息、支付宝免密续费服务等

具体流程如下:

  1. 用户打开客户端以后,客户端要求用户给予授权。
  2. 用户同意给予客户端授权。
  3. 客户端使用上一步获得的授权,向认证服务器申请令牌。
  4. 认证服务器对客户端进行认证以后,确认无误,同意发放令牌。
  5. 客户端使用令牌,向资源服务器申请获取资源。
  6. 资源服务器确认令牌无误,同意向客户端开放资源。

关键点在于 2. 和 3. ,即用户如何给客户端授权,一般有以下四种模式。

关于 OAuth 2.0 架构的其他细节请阅读 RFC 6749,本文仅对我认为比较重要的部分进行说明

客户端授权模式

授权码模式 (Authorization Code Grant)

授权码模式 (Authorization Code Grant) 是功能最完整、流程最严密的授权模式。它的特点就是通过客户端的后台服务器,与”服务提供商”的认证服务器进行互动。

授权码模式工作流

具体流程如下:

  1. 用户访问客户端,后者将前者导向认证服务器。
  2. 用户选择是否给予客户端授权。
  3. 假设用户给予授权,认证服务器将用户导向客户端事先指定的”重定向 URI”(redirection URI),同时附上一个授权码。
  4. 客户端收到授权码,附上早先的”重定向 URI”,向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见
  5. 认证服务器核对了授权码和重定向 URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token,可选,用来获取下一次的访问令牌)。

注意,这种方式需要在 HTTP 头信息中明确指定不得缓存

隐式授权模式 (Implicit Grant)

隐式授权模式 (Implicit Grant) 不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了”授权码”这个步骤,因此得名。所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证。

隐式授权模式工作流

具体流程如下:

  1. 客户端将用户导向认证服务器。
  2. 用户决定是否给于客户端授权。
  3. 假设用户给予授权,认证服务器将用户导向客户端指定的”重定向 URI”,并在 URI 的 Hash 部分包含了访问令牌。
  4. 浏览器向资源服务器发出请求,其中不包括上一步收到的 Hash 值。
  5. 资源服务器返回一个网页,其中包含的代码可以获取 Hash 值中的令牌。
  6. 浏览器执行上一步获得的脚本,提取出令牌。
  7. 浏览器将令牌发给客户端。

密码模式 (Resource Owner Password Credentials Grant)

密码模式 (Resource Owner Password Credentials Grant) 中,用户向客户端提供自己的用户名和密码。客户端使用这些信息,向”服务商提供商”索要授权。
在这种模式中,用户必须把自己的密码给客户端,但是客户端不得储存密码。这通常用在用户对客户端高度信任的情况下,比如客户端是操作系统的一部分,或者由一个著名公司出品。而认证服务器只有在其他授权模式无法执行的情况下,才能考虑使用这种模式。

密码模式工作流

具体流程如下:

  1. 用户向客户端提供用户名和密码。
  2. 客户端将用户名和密码发给认证服务器,向后者请求令牌。
  3. 认证服务器确认无误后,向客户端提供访问令牌。

客户端凭证模式 (Client Credentials Grant)

客户端凭证模式 (Client Credentials Grant) 指客户端以自己的名义,而不是以用户的名义,向”服务提供商”进行认证。严格地说,客户端模式并不属于 OAuth 框架所要解决的问题。在这种模式中,用户直接向客户端注册,客户端以自己的名义要求”服务提供商”提供服务,其实不存在授权问题。

客户端凭证模式工作流

具体流程如下:

  1. 客户端向认证服务器进行身份认证,并要求一个访问令牌。
  2. 认证服务器确认无误后,向客户端提供访问令牌。

四种授权模式异同

授权模式 安全性 适用场景 场景示例 复杂度/实现难度
授权码模式(Authorization Code Grant) 异步的方式进行认证和授权,适合安全性要求高、能够保护客户端密钥的情境。 用户在应用程序中点击“使用微信登录”,然后被重定向到微信的登录页面,用户登录后,应用程序收到一个授权码,用以获取访问令牌。
密码模式(Resource Owner Password Credentials Grant) 适中 客户端可以直接使用用户的凭据,适合受信任的客户端环境或需要与 API 直接交互的场景。 用户直接在客户端(如移动应用)中输入用户名和密码,客户端使用这些凭据直接向授权服务器请求访问令牌。
客户端凭证模式(Client Credentials Grant) 适中 适合服务间通信、不涉及用户信息的场景,如后端微服务之间的安全访问。 后端服务需要访问另一个 API,它使用自己的身份(客户端 ID 和密码)直接请求访问令牌,而不涉及用户。
隐式授权模式(Implicit Grant) 主要用于没有安全需求的公开客户端,如单页应用(SPA),但由于安全性较弱,现在不推荐作为主要授权方式使用。 前端 JavaScript 应用在浏览器中直接从授权服务器获取访问令牌,通常通过重定向响应 URL 来传递令牌。