1. OAuth 2.0 的四种授权模式
OAuth 2.0 定义了四种标准授权模式(Grant Types),每种模式适用于不同的应用场景和客户端类型。这四种模式的核心区别在于客户端能否安全保管密钥以及用户与客户端之间的信任程度。
1.1 授权码模式(Authorization Code)
这是最安全、最常用的模式,也是 OAuth 2.0 官方推荐的“黄金标准”。它专门针对有后端服务器的 Web 应用。
核心流程
- 用户访问第三方应用(客户端)。
- 应用将用户重定向到授权服务器(如微信/支付宝登录页)。
- 用户登录并授权。
- 授权服务器返回一个授权码(Authorization Code,通常有效期很短,如 10 分钟)给客户端后端。
- 客户端后端拿着这个授权码 + 客户端密钥(Client Secret)去换取访问令牌(Access Token)。
特点
- 安全性极高:访问令牌始终在后端与授权服务器之间传输,用户浏览器(前端)完全接触不到令牌。
- 支持刷新令牌:可以获取 Refresh Token 用于自动续期。
- 适用场景:绝大多数需要用户登录的网站(如“使用 GitHub 登录某技术社区”)、单页应用配合后端代理时。
1.2 隐式模式(Implicit)
这是授权码模式的简化版,主要针对没有后端服务器的纯前端应用(如纯静态页面、浏览器插件、移动端混合开发)。
注意: 在 OAuth 2.1 草案及现代安全实践中,该模式已被废弃,建议改用授权码模式 + PKCE 扩展。
核心流程
- 用户访问应用并点击授权。
- 授权服务器直接通过浏览器重定向的 URL 片段(Fragment) 返回访问令牌。
- 前端 JavaScript 从 URL 中提取令牌。
特点
- 安全性较低:令牌暴露在浏览器历史记录和网络日志中。
- 无刷新令牌:通常无法获取 Refresh Token,令牌过期后需重新授权。
- 适用场景:传统上用于纯前端应用、手机应用(早期),现在已逐渐被淘汰。
1.3 密码模式(Resource Owner Password Credentials)
该模式要求用户将用户名和密码直接提供给客户端,客户端再拿着这些凭证去换取访问令牌。
核心流程
- 用户在第三方应用界面输入自己的账号密码。
- 第三方应用拿着这个明文账号密码去授权服务器换取令牌。
特点
- 信任成本极高:用户必须完全信任该第三方应用(通常是官方自家应用),因为密码被第三方应用经手了。
- 存在风险:如果第三方应用是恶意的,用户的密码会被直接盗取。
- 限制:在 OAuth 2.1 中该模式已被明确废弃。
- 适用场景:仅适用于官方第一方应用(如支付宝 App 登录自己的后端),或是为了兼容老旧系统且无法改造的遗留项目。对于第三方接入的场景,严禁使用此模式。
1.4 客户端凭证模式(Client Credentials)
这是一种无人值守的模式,不涉及任何用户个人身份。它允许客户端以自己的名义去获取资源,而不是以用户的名义。
核心流程
- 客户端直接拿着自己的 Client ID 和 Client Secret 向授权服务器请求。
- 授权服务器验证客户端身份后,返回一个代表“该应用程序”的令牌。
特点
- 无用户参与:没有登录页面,也没有用户授权环节。
- 高权限:通常用于服务器与服务器之间的后端调用。
- 适用场景:API 网关调用微服务、定时任务同步数据、云服务之间的身份验证(例如 GitHub Actions 自动部署到云服务器),或者获取应用程序的全局配置数据(而非用户个人数据)。
补充说明:现代实践的变化
随着安全标准的演进,单纯的“四种模式”分类在实际落地中已经发生了如下变化:
-
PKCE 扩展
针对移动端 App 和单页应用,现在强烈推荐使用 授权码模式 + PKCE。它通过动态生成的code_verifier和code_challenge解决了前端应用无法安全保管 Client Secret 的问题,同时避免了隐式模式的安全隐患。目前主流的身份认证服务(如 Authing、Okta、Auth0)已将其作为移动端和单页应用的标准配置。 -
设备码模式(Device Code)
虽然不在“四种标准模式”的原定义中,但它是针对输入困难的设备(如智能电视、游戏机、打印机)的补充模式。用户在电视上看到码,在手机或电脑上访问特定网址输入该码,完成授权,电视端获取令牌。 -
模式废弃趋势
为了统一安全标准,隐式模式和密码模式在 OAuth 2.1 草案中已被移除。未来的安全架构中,核心将聚焦于 授权码模式(含 PKCE) 和 客户端凭证模式。
2. JWT 指南
JWT(JSON Web Token,RFC 7519)是一种紧凑、自包含的令牌格式,常用于在各方之间安全传递声明(如用户身份、权限等)。在微服务架构中,它非常适合用作无状态的认证凭证,帮助实现分布式系统中的身份验证与授权。
2.1 JWT 的结构
一个 JWT 由三部分组成,用 . 分隔:header.payload.signature
1. Header(头部)
通常包含令牌类型(typ)和签名算法(alg)。
2. Payload(载荷)
包含声明(claims),即要传递的信息。分为三类:
- 注册声明:
iss(签发者)、sub(主题,通常是用户ID)、exp(过期时间)、iat(签发时间)等。 - 公开声明:自定义字段,但建议避免冲突,如
user_id、role。 - 私有声明:双方约定的字段。
3. Signature(签名)
将编码后的 Header 和 Payload 用 . 拼接,使用密钥(HMAC)或私钥(RSA/ECDSA)进行签名。签名用于验证令牌的完整性和真实性,防止篡改。
最终一个 JWT 是一个由三部分组成的字符串,每部分经过 Base64Url 编码。
2.2 JWT 的工作流程
- 签发:用户登录成功后,认证服务生成 JWT(包含用户ID、角色、过期时间等),使用密钥签名,返回给客户端。
- 传输:客户端将 JWT 存储在合适位置(推荐
HttpOnly Cookie或内存,避免localStorage的 XSS 风险),后续请求放在Authorization: Bearer <token>头中。 - 验证:网关或资源服务器收到请求,从 Header 中提取 JWT,验证:
- 签名是否正确(使用共享密钥或公钥)
- 是否在有效期内(
exp) - (可选)
iss、aud等是否符合预期
- 使用:验证通过后,从 Payload 中提取用户身份和权限,进行业务处理。
2.3 JWT 的优缺点
优点
- 无状态:服务端不需要存储会话,方便水平扩展。
- 自包含:令牌中携带用户信息,减少查询数据库的开销。
- 跨语言:JSON 和签名算法在各语言都有成熟库。
- 灵活:支持多种签名算法(HS256、RS256、ES256 等)。
缺点
- 令牌体积较大:相比 Session ID,JWT 携带更多信息,可能增加网络开销。
- 无法主动吊销:在有效期内无法强制失效,除非引入黑名单机制(增加状态)。
- 敏感信息风险:Payload 仅 Base64Url 编码,未加密,严禁存放密码、身份证号等敏感信息。
- 有效期控制困难:短期令牌用户体验差,长期令牌风险高,通常需配合 Refresh Token。
与 Session 对比
| 维度 | JWT | Session |
|---|---|---|
| 存储 | 客户端,服务端无状态 | 服务端存储(内存/Redis),客户端存 Session ID |
| 扩展性 | 无需共享存储,易水平扩展 | 需共享 Session 存储(如 Redis) |
| 吊销 | 困难(需黑名单) | 简单(删除 Session) |
| 性能 | 签名验证有计算开销,但无网络 I/O | 每次请求查询 Redis,有网络开销 |
| 适用场景 | 分布式系统、移动端、微服务网关 | 传统单体、对吊销要求严格的场景 |