19、深入理解 JWT 与 OAuth2 授权机制

深入理解 JWT 与 OAuth2 授权机制

在当今的 API 开发和安全领域,JSON Web Tokens (JWT) 和 OAuth2 授权机制扮演着至关重要的角色。本文将详细介绍 JWT 的相关知识,以及 OAuth2 中不同授权机制的工作原理和应用场景。

1. JSON Web Tokens (JWT)

JWT 是一种由 RFC 标准化的令牌格式,也是 OAuth2 的事实上的标准令牌。它由声明(claims)和相关值组成,结构和编码遵循特定标准,以确保令牌不可修改,还可以进行加密。JWT 在“空间受限的环境,如 HTTP 授权头”中传输信息时特别有用。

以下是一个 JWT 的示例:

{
    "iss": "http://mastering-api/",
    "sub": "18f913b1-7a9d-47e6-a062-5381d1e21ffa",
    "aud": "Attendee-Service",
    "exp": 1618146900,
    "nbf": 1618144200,
    "iat": 1618144200,
    "jti": "4d13ba71-54e4-4583-9458-562cbf0ba4e4"
}

在这个示例中, iss sub aud exp nbf iat jti 是 JWT RFC 中的保留声明,它们具有特殊含义,但并非令牌中必需的,不过能提供最少信息的起始点。下面是这些声明缩写的含义和典型用途:
| 声明缩写 | 含义 | 典型用途 |
| ---- | ---- | ---- |
| iss (Issuer) | 颁发令牌的权威机构 | 通常是身份提供者,如 Google 或 Auth0 |
| sub (Subject) | 用于标识 JWT 主体的唯一标识符 | 对于代表用户的移动应用,可能是参会者;对于服务器到服务器的连接,可能是应用程序 |
| aud (Audience) | 令牌的预期接收者 | 指明令牌是为谁准备的 |
| exp (Expiration time) | 令牌的过期时间 | 例如,在本示例中是颁发后 45 分钟 |
| nbf (Not before) | 令牌在该时间之前不可使用 | 本示例中与颁发时间相同 |
| iat (Issued at) | 令牌的颁发时间 | 记录令牌何时被颁发 |
| jti (JWT ID) | JWT 的唯一标识符 | 用于唯一标识该 JWT |

需要注意的是,令牌还可以包含更多信息,如用户的首选名称、电子邮件、颁发方的声明以及请求令牌的应用程序等。对于高安全性的 API,授权服务器的身份验证方法通常也是一个声明,可用于检查资源所有者是否使用了多因素身份验证(MFA)。

1.1 JWT 的编码和验证

JWT 有两种流行的编码机制:
- JSON Web Signatures (JWS) :为 JWT 提供完整性。令牌的内容对任何接收者都是可见的,但声明经过数字签名,确保如果令牌内容被更改,令牌立即无效。
- JSON Web Encryption (JWE) :提供完整性,同时对令牌进行加密,意味着令牌内容无法被查看。

一般来说,使用 JWT 通常指使用 JWS,而加密的 JWT 则指使用 JWE。最常用的机制是 JWS,其中数字签名使用私钥进行,接收者使用公钥验证令牌是否由特定颁发方签名。公钥可以自由共享给需要验证令牌完整性的任何一方。

警告 :如果使用 JWS 编码的 JWT,不要在声明值中插入机密数据,因为任何拥有 JWT 的人都可以读取声明。若要确保 JWT 不可读,应使用 JWE。

1.2 JWT 的验证步骤

当接收到 JWT 时,需要进行多部分验证:
1. 检查签名 :确认令牌是由预期的颁发方颁发,且未被修改或篡改。
2. 验证其他声明 :检查令牌是否未过期( exp 声明),以及是否在允许使用的时间之后( nbf 声明)。

所有颁发的令牌都应该是短寿命的,因为长寿命的令牌如果丢失或被盗,会带来风险。虽然没有官方标准规定短寿命或长寿命令牌的有效期,但通常建议短寿命令牌的有效期在 1 到 60 分钟之间,长寿命令牌的有效期为 1 年到 10 年。应尽可能缩短令牌的寿命。

2. OAuth2 授权机制

OAuth2 是一种可扩展的授权协议,2012 年发布的官方规范包含四种授权类型,此后又有额外的授权类型和修改被批准以扩展其用途。OAuth2 呈现了一个抽象协议,其流程如下:

graph LR
    A[客户端] -->|请求授权| B[资源所有者]
    B -->|授予/拒绝访问| A
    A -->|请求访问令牌| C[授权服务器]
    C -->|颁发访问令牌| A
    A -->|携带访问令牌请求资源| D[资源服务器(API)]
    D -->|返回资源| A

这个抽象协议强调了资源所有者对自己资源的控制权。客户端向资源所有者请求授权,即询问“我(应用程序)能否代表您访问您的资源?”授权的方式并不重要,关键是资源所有者有机会授予或拒绝访问。当向资源服务器请求资源时,客户端如何获得访问令牌并不重要,只要请求中包含有效的访问令牌,资源服务器就会返回资源。每个步骤都是独立的,不需要了解前一个步骤的信息。这就是为什么针对不同场景有不同的授权类型,以确保在不同环境中这些步骤的安全性。

2.1 是否使用 OAuth2 的决策指南

在决定是否使用 OAuth2 时,可以参考以下决策指南:
| 决策 | 讨论要点 | 建议 |
| ---- | ---- | ---- |
| 是否使用 OAuth2 或是否有其他更适合操作环境的身份验证和授权标准? | 1. 检查当前的安全要求以及可能的变化,例如 API 是仅在控制平面内使用,还是也在控制平面外/可能与第三方一起使用。
2. 预期支持的安全模型是什么?外部集成商是否要求使用特定的安全模型?
3. 是否需要支持多种身份验证和授权模型?这在从现有身份验证模型迁移到另一个模型时很重要。 | 使用 OAuth2 将与其他 API 用户实现最大兼容性。它是一个行业标准,有文档和客户端库,便于集成。OAuth2 支持终端用户和系统到系统的场景。 |

2.2 授权码授权(Authorization Code Grant)

授权码授权是 OAuth2 授权的一种实现,也是前面抽象协议的一个具体实现。这是最著名的授权类型,你可能在不知不觉中使用过。其典型用例是由服务器支持的网站,该网站不向互联网公开(即可以保护机密)。能够保护机密的客户端应用程序称为机密客户端。

授权码授权的工作流程如下:
1. 客户端应用程序将 Web 浏览器(图中的用户代理)重定向到授权服务器。重定向请求将包含客户端的标识(客户端 ID),并指明使用的授权类型(在此例中为授权码授权,即 code )。
2. 授权服务器要求资源所有者(终端用户)进行身份验证,以确定资源所有者的身份。然后,授权服务器可以获得资源所有者的同意,如果他们授予客户端应用程序授权。
3. 假设授权被授予,授权码通过用户代理传递给客户端应用程序。
4. 客户端向授权服务器请求访问令牌,并出示授权码。客户端应用程序必须使用与授权服务器共享的机密进行身份验证。
5. 如果客户端应用程序成功进行身份验证并出示有效的授权码,将被授予访问令牌。

graph LR
    A[客户端应用程序] -->|重定向到授权服务器| B[授权服务器]
    B -->|要求身份验证| C[资源所有者(终端用户)]
    C -->|身份验证并同意授权| B
    B -->|返回授权码| A
    A -->|出示授权码请求访问令牌| B
    B -->|颁发访问令牌| A

然而,随着单页应用(SPA)的出现,由于 SPA 网站基于 JavaScript 运行在用户浏览器中,源代码对用户完全可见,无法保护机密,因此传统的授权码授权不再适用。

2.3 授权码授权 + PKCE

为了解决 SPA 应用的安全问题,引入了授权码授权 + PKCE(Proof Key for Code Exchange)。PKCE 用于减轻拦截攻击。在授权码授权 + PKCE 中,需要两个额外的参数:
- code_challenge :用于授权请求。
- code_verifier :用于访问令牌请求。

code_verifier 是客户端生成的加密随机字符串, code_challenge code_verifier 的哈希值。

授权码授权 + PKCE 的工作流程如下:
1. 客户端发起授权请求,并将 code_verifier 发送到授权服务器。 code_verifier 经过转换(通常是哈希)得到 code_challenge
2. 与授权码授权一样,返回授权码。
3. 客户端通过发送授权码和 code_verifier 请求访问令牌,由于是公共客户端,不发送客户端机密。
4. 授权服务器颁发访问令牌给客户端应用程序。

graph LR
    A[客户端应用程序] -->|发送 code_verifier 并请求授权| B[授权服务器]
    B -->|返回授权码| A
    A -->|发送授权码和 code_verifier 请求访问令牌| B
    B -->|颁发访问令牌| A

提示 :PKCE 必须用于公共客户端,但也可以用于机密客户端以提供额外保护。授权码授权及其 PKCE 扩展适用于大多数包含终端用户的公共和机密客户端场景。

2.4 案例研究:使用授权码授权访问参会者 API

有两个客户端应用程序用于访问参会者 API,它们都使用授权码授权代表用户(资源所有者)访问 API:
- 外部 CFP 系统 :是一个机密客户端,可以维护机密,因此可以使用授权码授权。
- 移动应用程序 :是一个公共客户端,无法维护机密,因此必须使用授权码授权 + PKCE。

通过这个案例可以看出,使用 PKCE 并不会改变高级步骤或用户的操作流程。

3. 刷新令牌(Refresh Tokens)

颁发短寿命的令牌是良好的实践,但要求用户反复输入用户名和密码会带来不好的体验。刷新令牌是一种长寿命的令牌,客户端可以在之前的访问令牌过期时使用它请求额外的访问令牌。刷新令牌是授权请求的一部分,这意味着终端用户不需要参与后续访问令牌的请求。

根据最新的安全最佳实践,一旦检测到刷新令牌被使用两次,将立即撤销当前的刷新令牌。刷新令牌是额外的凭证,且寿命较长,因此必须确保其安全,避免泄露。如果需要拒绝客户端的访问,包括资源所有者不想让客户端继续访问其资源时,可以撤销刷新令牌。这样,下次客户端应用程序请求新的访问令牌(短寿命)时将被阻止。这也意味着可能存在一个窗口,客户端拥有有效的访问令牌,但实际上不应被允许访问,这就是为什么短寿命令牌很重要的原因。

4. 客户端凭证授权(Client Credentials Grant)

客户端凭证授权的客户端是机密客户端,因为它需要维护机密。这种授权类型适用于机器到机器的通信,连接预先设置,客户端的访问权限也预先安排好。

客户端获取访问令牌的过程非常简单:
1. 客户端应用程序向授权服务器进行身份验证,并请求访问令牌。客户端还需指明使用的授权类型为 client_credentials
2. 如果客户端应用程序成功进行身份验证,授权服务器将返回访问令牌。

graph LR
    A[客户端应用程序] -->|身份验证并请求访问令牌| B[授权服务器]
    B -->|颁发访问令牌| A

由于没有资源所有者需要给予许可,因此没有额外的步骤。客户端代表自己行事,只需识别自己。

4.1 案例研究:使用客户端凭证授权从 CFP 系统访问参会者 API

外部 CFP 系统每三个月生成一份报告,显示有多少参会者提交演讲并成为演讲者。这个报告生成不是代表参会者,而是为了外部 CFP 系统自身。客户端(外部 CFP 系统)在授权服务器中注册,在参会者服务中,该客户端被添加到可以访问服务的客户端列表中,并被配置为能够读取参会者信息和查询哪些用户提交了演讲。当客户端想要访问参会者 API 时,它将向授权服务器请求访问令牌,然后在调用参会者 API 时使用该访问令牌。

提示 :客户端凭证授权不使用刷新令牌,而是客户端直接请求新的访问令牌。

综上所述,JWT 和 OAuth2 提供了强大的安全机制和灵活的授权方式,适用于各种不同的应用场景。在实际应用中,需要根据具体的安全需求和业务场景选择合适的授权类型和令牌管理方式。

深入理解 JWT 与 OAuth2 授权机制

5. 不同授权类型的对比

为了更清晰地了解各种 OAuth2 授权类型的特点和适用场景,我们可以通过以下表格进行对比:
| 授权类型 | 客户端类型 | 适用场景 | 安全性 | 主要步骤 |
| ---- | ---- | ---- | ---- | ---- |
| 授权码授权 | 机密客户端 | 由服务器支持的网站,可保护机密 | 较高,使用授权码和客户端机密 | 1. 客户端重定向到授权服务器
2. 资源所有者身份验证并授权
3. 返回授权码
4. 客户端用授权码请求访问令牌
5. 颁发访问令牌 |
| 授权码授权 + PKCE | 公共客户端 | 单页应用(SPA)等无法保护机密的应用 | 高,使用 PKCE 减轻拦截攻击 | 1. 客户端发送 code_verifier 并请求授权
2. 返回授权码
3. 客户端用授权码和 code_verifier 请求访问令牌
4. 颁发访问令牌 |
| 客户端凭证授权 | 机密客户端 | 机器到机器的通信 | 较高,依赖客户端机密 | 1. 客户端身份验证并请求访问令牌
2. 颁发访问令牌 |

通过这个表格,我们可以根据不同的客户端类型和应用场景,快速选择合适的授权类型。

6. 选择合适的授权类型

在实际应用中,选择合适的 OAuth2 授权类型需要考虑多个因素,以下是一些建议:
- 客户端类型
- 如果客户端能够保护机密(如服务器端应用),可以考虑授权码授权或客户端凭证授权。
- 如果客户端无法保护机密(如 SPA 或移动应用),则应使用授权码授权 + PKCE。
- 应用场景
- 涉及终端用户交互的场景,如网站登录、第三方应用授权等,授权码授权及其扩展是较好的选择。
- 机器到机器的通信,如系统之间的数据同步、服务调用等,客户端凭证授权更为合适。
- 安全性要求
- 对于安全性要求较高的场景,如金融交易、敏感数据访问等,应使用加密的 JWT(JWE)和更严格的授权验证机制。
- 对于一般的应用场景,JWS 编码的 JWT 通常可以满足需求。

7. 令牌管理的最佳实践

无论是 JWT 还是刷新令牌,都需要进行有效的管理,以确保系统的安全性和性能。以下是一些令牌管理的最佳实践:
- 短寿命令牌 :所有颁发的令牌都应该是短寿命的,以减少令牌丢失或被盗的风险。建议短寿命令牌的有效期在 1 到 60 分钟之间。
- 刷新令牌的安全 :刷新令牌是长寿命的,需要特别注意其安全性。确保刷新令牌存储在安全的地方,避免泄露。同时,检测到刷新令牌被使用两次时,立即撤销当前的刷新令牌。
- 定期轮换密钥 :对于 JWS 编码的 JWT,定期轮换私钥和公钥可以提高安全性。
- 监控和审计 :对令牌的颁发、使用和撤销进行监控和审计,及时发现异常行为。

8. 总结

JWT 和 OAuth2 是现代 API 开发和安全领域中非常重要的技术。JWT 提供了一种方便、安全的令牌格式,可用于在不同系统之间传递信息。OAuth2 则提供了灵活的授权机制,适用于各种不同的应用场景。

通过本文的介绍,我们了解了 JWT 的结构、编码和验证方法,以及 OAuth2 中不同授权类型的工作原理和适用场景。在选择授权类型时,需要考虑客户端类型、应用场景和安全性要求等因素。同时,有效的令牌管理也是确保系统安全的关键。

希望本文能够帮助你更好地理解 JWT 和 OAuth2,并在实际应用中做出正确的选择。

graph LR
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;

    A([开始]):::startend --> B{选择授权类型}:::decision
    B -->|机密客户端, 网站应用| C(授权码授权):::process
    B -->|公共客户端, SPA 应用| D(授权码授权 + PKCE):::process
    B -->|机密客户端, 机器通信| E(客户端凭证授权):::process
    C --> F(获取访问令牌):::process
    D --> F
    E --> F
    F --> G(使用访问令牌访问资源):::process
    G --> H{令牌是否过期}:::decision
    H -->|是| I(使用刷新令牌获取新访问令牌):::process
    H -->|否| G
    I --> G
    G --> J([结束]):::startend

这个流程图展示了从选择授权类型到使用令牌访问资源,再到处理令牌过期的整个过程,帮助我们更直观地理解整个授权和令牌管理的流程。

在实际应用中,我们可以根据具体的业务需求和安全要求,灵活运用这些技术,构建安全、可靠的 API 系统。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值