手记

一步步精通OAuth 2.0:从入门到实践

](https://leapcell.io/?lc_t=d_oauth)

修正为:

查看链接

Leapcell:新一代的无服务器平台,专门用于网页托管、异步任务,并且支持Redis

OAuth 2.0 的简单易懂解释

OAuth 是一种广泛使用的全球开放网络授权标准,当前版本为 2.0。我们将基于 RFC 6749,简明扼要地解释 OAuth 2.0 的设计理念。

一. 应用场景

为了帮助理解OAuth适用的情境,我们先来看一个假设的例子。假设有一个名为“云照片打印”的网站,能够打印用户存储在谷歌上的照片。如果用户想要使用这项服务,就需要允许“云照片打印”读取他们存储在谷歌上的照片。

这里有一个关键问题:Google 只会在获得用户授权后,才允许“云照片打印”读取这些照片。这样,“云照片打印”怎么才能得到用户的授权呢?

传统做法是由用户提供或分享他们的 Google 用户名和密码给“云端照片打印”,这样“云端照片打印”可以访问用户的照片。然而,这种方法存在很多严重的问题。

  1. 密码存储的安全风险:为了提供后续服务,"云照片打印"将存储用户的密码,这带来了很大的安全风险。
  2. 登录方式的安全问题:谷歌只能使用密码登录方式,但简单的密码登录安全性较差。
  3. 缺乏权限控制:"云照片打印"可以访问用户存储在谷歌上的所有资料,用户无法限制"云照片打印"所获得授权的范围和有效期。
  4. 撤销权限成本高:用户只能通过更改密码来撤销给予"云照片打印"的权限,这会导致所有其他已授权的第三方应用失效。
  5. 数据泄露风险高:一旦一个第三方应用被破解,就会导致用户的密码和受密码保护的所有数据泄露。

为了解决这些问题,OAuth 也因此诞生了。

2. 名词解释

在我们详细了解OAuth 2.0之前,首先需要理解几个专用名词。理解它们对于理解相关图表非常重要。

  1. 第三方应用(Third-party Application):本文中也称为“客户端”,例如在上一节的例子中提到的“云照片打印”。
  2. HTTP服务提供者(HTTP Service Provider):本文中简称为“服务提供商”,例如在上一节的例子中提到的Google。
  3. 资源拥有者(Resource Owner):本文中也称为“用户”。
  4. 用户代理(User Agent):在本文中指的是浏览器。
  5. 认证服务器(Authorization Server):即服务提供商用来处理认证的专用服务器。
  6. 资源服务器(Resource Server):即服务提供商存储用户生成的资源的服务器。它可以与认证服务器是同一台服务器,也可以是不同的服务器。

理解了上述名词之后,理解OAuth的作用也就变得容易了,即让“客户端”能够安全且可控地从“用户”那里获取授权,然后就能与“服务提供商”进行交互了。

III. OAuth概念

OAuth 在“客户端”和“服务提供商”之间建立了一个授权层。“客户端”无法直接登录到“服务提供商”,而是登录到授权层,以便将用户与客户端区分开来。客户端用来登录授权层的令牌不同于用户的密码,用户在登录时还可以指定授权层令牌的有效期和权限范围。

在“客户端”登录到授权层后,“服务提供者”将会根据令牌的有效权限范围,向“客户端”展示用户的存储材料。

IV. 操作过程

OAuth 2.0的操作过程如下图,摘录自RFC 6749:

  1. (A) :用户打开客户端时,客户端向用户请求授权。
  2. (B) :用户同意给客户端授权。
  3. (C) :客户端利用刚才获得的授权向认证服务器申请令牌。
  4. (D) :认证服务器确认客户端身份无误后,同意发放令牌。
  5. (E) :客户端用令牌向资源服务器申请资源。
  6. (F) :资源服务器确认令牌无误后,同意向客户端提供资源。

在这六个步骤中,步骤B尤为关键,即用户如何给客户端授权。有了这个授权,客户端就能拿到token,再用token去获取资源。接下来我会一一解释客户端获取授权的四种方式。

客户端授权模式

客户端必须在获得用户授权(授权码)后才能获取访问令牌(access token)。OAuth 2.0 规定了以下四种授权方式:

  1. 授权码模式
  2. 隐式模式
  3. 资源所有者密码模式
  4. 客户端凭证模式
VI. 授权码方式

授权码模式(授权码)是功能最全面、流程最严谨的授权方式之一。其特点是通过客户端后台与服务商的认证服务器进行后台交互。具体流程如下:

  1. (A) : 用户访问客户端,客户端将用户重定向到认证服务器。
  2. (B) : 用户决定是否允许客户端访问。
  3. (C) : 如果用户同意授权,认证服务器将用户重定向到客户端预先指定的“重定向URI”(redirect URI),同时附带一个授权码。
  4. (D) : 客户端接收到授权码,附带之前的“重定向URI”,并向认证服务器申请令牌。这一步在客户端后台自动完成,用户不会看到。
  5. (E) : 认证服务器检查授权码和重定向URI。如果一切正常,它将访问令牌(access token)和刷新令牌(refresh token)发送给客户端。

以下是所需参数,用于上述步骤:

  • 步骤A:客户端申请认证的URI包含以下参数:

  • response_type:表示授权类型,是一个必填参数,值固定为“code”。

  • client_id:表示客户端的ID,是一个必填参数。

  • redirect_uri:表示重定向的URI,是一个可选的。

  • scope:表示请求的权限范围,是一个可选的。

  • state:表示客户端当前状态,可以指定任意值。认证服务器会原封不动地返回这个值。

比如说:

    GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
    Host: server.leapcell.io

进入全屏,退出全屏

  • 步骤 C :服务器响应客户端时,URI 包含以下参数:

  • code :表示授权码,这是一个必填参数。此代码的有效期非常短,一般设定为 10 分钟,客户端只能使用一次此代码,否则会被授权服务器拒绝。此代码与客户端 ID 和重定向 URI 一一对应关系。

  • state :如果客户端请求中包含此参数,认证服务器也必须在响应中包含同样的参数。

例如:

HTTP/1.1 302 Found
Location: https://client.example.com/cb?code=SplxlqweqwWxSbIA&state=xyz

点击切换到全屏,点击切换出全屏

  • 步骤 D:客户端向认证服务器发起的用于申请 token 的 HTTP 请求包含以下参数:

  • grant_type :表示授权模式,为必填项,值固定为 "authorization_code"。

  • code :上一步获取的授权码,为必填项。

  • redirect_uri :表示重定向 URI,为必填项,必须与步骤 A 中相同的参数值保持一致。

  • client_id :表示客户端的 ID,为必填项。

比如说:

POST /token HTTP/1.1
Host: server.leapcell.io
Authorization: Basic czZCaGRrwrqw0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb

全屏显示,退出全屏

  • 步骤 E :认证服务器返回的 HTTP 响应包含以下参数:

  • access_token :表示访问令牌,这是必需的参数。

  • token_type :表示令牌类型。此值不区分大小写,这是必需的参数。它可以是 bearer 类型或 mac 类型。

  • expires_in :表示过期时间(以秒为单位)。如果缺少此参数,则必须通过其他方式设置过期时间。

  • refresh_token :表示刷新令牌,用于获取下一个访问令牌,是可选的参数。

  • scope :表示权限范围。如果与客户端请求的一致,可以省略。

比如说:

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8  // 内容类型: application/json;charset=UTF-8
Cache-Control: no-store  // 缓存控制: 不存储
Pragma: no-cache  // Pragma: 不缓存
{
    "access_token":"2YotnFqwrwqrqwCsicMWpAA"  // 访问令牌: 2YotnFqwrwqrqwCsicMWpAA
    "token_type":"example",
    "expires_in":3600  // 有效时间: 3600秒
    "refresh_token":"tGzv3JOqweqweTlKWIA"  // 刷新令牌: tGzv3JOqweqweTlKWIA
    "example_parameter":"example_value"  // 示例参数: 示例值
}

进入全屏,退出全屏

从上面的代码可以看出,相关参数通过JSON格式(Content-Type: application/json)发送,并且在HTTP头部信息中明确禁止了缓存。

VII. 简易模式

简化模式(隐式授予类型)不需要经过第三方应用的服务器,而是直接在浏览器中从认证服务器申请令牌,跳过了“授权码”这一步,因此得名。所有步骤都在浏览器中完成,令牌在浏览器中对用户可见,客户端不需要进行身份验证。具体的操作步骤如下:

  1. (A):客户端会引导用户前往认证服务器进行验证。
  2. (B):用户选择是否同意客户端的请求。
  3. (C):如果用户同意了,认证服务器将用户重定向到客户端指定的“重定向URI”,并将访问令牌(access token)包含在URI的Hash部分。
  4. (D):浏览器向资源服务器发出请求,不过请求中不包含之前的Hash值。
  5. (E):资源服务器返回一个网页,该网页上的代码可以从Hash值中提取令牌。
  6. (F):浏览器执行获取的代码,从而提取令牌。
  7. (G):浏览器将令牌传递给客户端。

下面列出的是做这些步骤所需要的一些参数:

  • 步骤A:客户端发送的HTTP请求包含以下参数:

  • response_type:表示授权的类型,此处值固定为"token",为必填参数。

  • client_id:表示客户端的ID,为必填参数。

  • redirect_uri:表示重定向的URI,为可选参数。

  • scope:表示权限的范围,为可选参数。

  • state:表示客户端当前状态,可以是任意值。认证服务器会原封不动地返回这个值。

例如:

GET /authorize?response_type=token&client_id=s6BhdRkqt3&state=xyz&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.leapcell.io

全屏模式, 退出

  • 步骤 C:认证服务器回应客户端的 URI 包含以下参数:

  • access_token:表示访问令牌,这是一个必须的参数。

  • token_type:表示令牌类型。该值不区分大小写,是一个必须的参数。

  • expires_in:表示过期时间,以秒为单位。如果省略此参数,则过期时间必须通过其他方式设置。

  • scope:表示权限范围。如果与客户端请求的权限范围相同,则此项目可省略。

  • state:如果客户端请求中包含此参数,则认证服务器的响应也必须包含同样的参数。

比如说:

HTTP/1.1 302 Found(表示请求的资源已被临时移动到新的 URI)
Location: http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA&state=xyz&token_type=example&expires_in=3600(位置)
# access_token=2YotnFZFEjr1zCsicMWpAA(访问令牌)
# state=xyz(状态参数)
# token_type=example(令牌类型)
# expires_in=3600(过期时间,以秒为单位)

点击全屏显示,再点击退出全屏

在上述示例中,认证服务器利用HTTP头信息中的Location字段来指定浏览器重定向到的URL。请注意,此URL中的Hash部分包含令牌。根据步骤D,浏览器将在下一步访问Location指定的URL,但浏览器不会发送该URL中的Hash部分。在下一步E中,服务提供商的资源服务器发送的代码将从Hash部分中提取令牌。

VIII. 密码方式(Password方式)

在密码模式(资源所有者密码凭证模式)中,用户将自己的用户名和密码提供给客户端,客户端使用这些信息向“服务提供商”请求授权。在这种模式下,用户必须向客户端提供密码,但客户端不能存储密码。这通常在用户对客户端高度信任的情况下,例如客户端是操作系统的一部分或由知名公司生产。只有在其他授权模式不可用时,认证服务器才会考虑使用这种模式。接下来让我们看看具体步骤:

  1. (A):用户向客户端提供用户名和密码。
  2. (B):客户端向认证服务器发送用户名和密码,并请求令牌。
  3. (C):认证服务器确认一切正常后,向客户端提供访问令牌。

在步骤 B 中,客户端发送的 HTTP (超文本传输协议) 请求包含了以下参数:等等。

  • grant_type : 表示授权类型,这里的值固定为 "password",为必填参数。
  • username : 表示用户名,为必填参数。
  • password : 表示密码,为必填参数。
  • scope : 表示权限范围,为可选参数。

比如说,

POST /token HTTP/1.1
Host: server.leapcell.io
Authorization: Basic czZCaGRSa3F0gertetewrmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=password&username=johndoe&password=A3ddj3w

切换到全屏 退出全屏

在步骤 C 里,认证服务器给客户端发了一个访问令牌,比如:

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
    "access_token":"2YotnFZqweqwreqwrpAA",
    "token_type":"example",
    "expires_in":3600,
    "refresh_token":"tGzv3rewrewrtr2TlKWIA",
    "example_parameter":"example_value"
}

进入全屏 退出全屏

上述代码中各个参数的意义可以参考《授权码模式》部分。在整个流程中,客户端程序不能保存用户的密码。

第九. 客户模式

客户端凭证模式意味着客户端直接用自己的名义,而不是用户的名义,对服务提供方进行认证。严格来说,客户端凭证模式不属于OAuth框架需要解决的问题。在这种模式下,用户直接在客户端注册,客户端以自己的名义向服务商请求服务。实际上,这里没有涉及授权问题。具体步骤如下:

  1. (A):客户端使用认证服务器验证身份并请求令牌。
  2. (B):认证服务器验证通过后,向客户端提供令牌。

在步骤 A 中,客户端发送的 HTTP 请求中包含以下参数:

  • grant_type : 表示授权类型,此处的值必须是“client_credentials”,是必填项。
  • scope : 表示权限范围的参数,是可选项。

比如说:

这是一个向服务器发送客户端凭证请求令牌的POST请求。
POST /token HTTP/1.1
Host: server.leapcell.io
Authorization: Basic czZCaGRSqeqwewqmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials

切换到全屏 退出全屏

认证服务器必须通过某种方法验证用户身份。

例如,在步骤 B 里,认证服务器会向客户端发送一个访问令牌:

# 下面是一个HTTP响应示例
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
    "access_token":"2Yotnqweqwe1zCsicMWpAA",
    "token类型":"example",
    "expires_in":3600,
    "示例参数":"example_value"
}

点击全屏 点击退出全屏

上述代码中各个参数的意义请参考“授权模式部分(章节)”。

更新令牌(Token)

如果用户访问时客户端的“访问令牌”已过期,则需要使用“刷新 token”申请新的访问令牌。用于更新令牌的 HTTP 请求包含以下参数:具体来说,这些参数包括。

  • grant_type : 表示授权模式,这里的值必须是 "refresh_token",为必填参数。
  • refresh_token : 表示之前获得的刷新令牌,为必填参数。
  • scope : 表示请求的授权范围,不能超过之前申请的范围。如果省略此参数,默认与之前相同。

比如说:

POST /token HTTP/1.1 (HTTP请求方法,用于发送数据到服务器)
Host: server.example.com (主机名,指定服务器地址)
Authorization: Basic czZCaGRSa3F0MzqeqweqwmF0M2JW (授权信息,用于身份验证)
Content-Type: application/x-www-form-urlencoded (内容类型,指定数据格式)
grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA (请求参数,包含刷新令牌)

点击全屏切换(进入或退出)

Leapcell: 全新无服务器平台,适用于网站托管、异步任务处理和 Redis,点击了解更多

最后,我推荐的最佳部署平台是Leapcell,我觉得Leapcell就挺好的。


点击这里: https://leapcell.io/?lc_t=d_oauth

1. 支持多种语言

  • 可用 JavaScript、Python、Go 或 Rust 来开发。

2. 您可以免费无限部署项目

  • 只按使用付费 — 不用不计费,就这么简单。

3. 超高的性价比

  • 按需付费,无闲置时间费用。
  • 示例:$25可以支持6.94M请求,平均响应时间为60毫秒。

4. 精简的开发者体验

  • 直观的用户界面,轻松完成设置。
  • 全自动的CI/CD流水线和GitOps集成。
  • 实时指标和日志,提供实用的洞察。

5. 轻松可扩展性和高性能表现

  • 自动扩展轻松应对高并发。
  • 零运营负担 — 只需专注于构建。

(https://leapcell.io/?lc_t=d_oauth)

更多详情请查看这里!

Leapcell 推特: https://x.com/LeapcellHQ

0人推荐
随时随地看视频
慕课网APP