什么是令牌?
在介绍不透明令牌之前,了解什么是令牌很重要:
令牌用于表示和传递各方之间的安全信息,它们支持在互联网幕后的绝大多数认证 (Authentication) 和授权 (Authorization) 过程。在 web 服务中,最受欢迎的两种令牌类型是 RFC 7519: JSON Web Tokens (JWT) 和不透明令牌。
什么是不透明令牌?
不透明令牌是采用专有格式的令牌,你无法访问,通常包含服务器持久存储信息的一些标识符。
不透明令牌是令牌可能采用的一种形式,访问令牌 (Access token) 和刷新令牌 (Refresh token) 可以作为不透明令牌存在。不透明令牌的格式由其发行者决定,通常是用于帮助发行者在数据库中检索和识别某些信息的数字和/或字符字符串。以下是不透明令牌的示例:
M-oxIny1RfaFbmjMX54L8Pl-KQEPeQvF6awzjWFA3iq
另一方面,JWT 是另一种常见的令牌格式。它是包含所有声明和信息以及来自发行者的签名的 JSON 字符串。默认情况下,它不是加密的,但可以使用 JSON Web Encryption (JWE) 标准进行加密。即使 JWT 通常未加密,也不会影响其安全性——签名的存在确保了令牌内容的完整性,从而可以完全信任 JWT 中的数据。
与 JWT 不同的是,JWT 包含所有直接在受保护资源上验证所需的信息,而不透明令牌不能被资源直接验证。相反,它们需要不透明令牌的发行者(通常是授权服务器 (Authorization server))进行验证。这种验证过程通常称为令牌内省 (Token introspection)。
什么是 JWT?
与不透明令牌相反,JWT 是一种自含(stateless)的令牌,以结构化和可读的格式携带信息。
JWT 由三部分组成:header
、payload
和 signature
,每个部分采用 Base64URL 编码。
以下是一个 JWT 的示例:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
header
包含关于令牌类型和用于签名的算法的信息。例如,{"alg": "HS256", "typ": "JWT"}
。payload
部分包含声明——关于用户或授权的信息片段——如用户 ID、过期时间和范围 (Scopes)。由于这些数据是编码的而不是加密的,任何拥有令牌的人都可以解码它来查看声明,尽管他们无法在不使签名失效的情况下更改它。根据规范和授权服务器配置,payload 中可以包含各种声明。这赋予了令牌自包含的特性。例如,{"sub": "1234567890", "name": "John Doe", "iat": 1516239022}
。signature
是通过使用指定算法组合 header、payload 和密钥生成的。此签名用于验证令牌的完整性,并确保其未被篡改。
JWT 普遍被使用,因为它们可以在本地由客户端或任何服务进行验证,而无需与授权服务器交互。这使得 JWT 在需要多个服务独立验证令牌真实性的分布式系统中特别有效。
然而,这种便利也带来了确保令牌声明不过度暴露的责任,因为它们对任何拥有令牌的人都是可见的。此外,JWT 通常是短期有效的,过期时间包含在令牌的声明中,以确保令牌不是无限期有效的。
不透明访问令牌验证
不透明访问令牌通过将其发送回授权服务器进行验证来进行验证。授权服务器维护已发令牌的状态,并可以根据其内部存储确定令牌的有效性。
- 客户端向授权服务器请求访问令牌。
- 授权服务器颁发不透明令牌。
- 客户端在请求头中发送资源访问请求和不透明令牌。
- 资源提供者发送令牌内省 ( RFC 7662: OAuth 2.0 Token Introspection ) 请求到授权服务器以验证令牌。
- 授权服务器响应令牌信息。
JWT 访问令牌验证(离线)
JWT 访问令牌可以通过拥有令牌的公钥的客户端或任何服务进行离线验证。
- 资源提供者从 OIDC 发现端点预取授权服务器的公钥。此公钥用于验证令牌的签名并确保其完整性。
- 客户端向授权服务器请求访问令牌。
- 授权服务器签发 JWT 令牌。
- 客户端在请求头中发送资源访问请求和 JWT 令牌。
- 资源提供者使用从授权服务器获得的公钥解码并验证 JWT 令牌。
- 资源提供者根据令牌的有效性授予访问权限。
OIDC 中的使用场景
在 OIDC (OpenID Connect) 的上下文中,不透明令牌和 JWT 有不同的用途,并在不同场景中使用。
不透明令牌
- 用户资料检索:
默认情况下,当客户端请求访问令牌而不指定资源并包括 openid
范围时,授权服务器颁发不透明访问令牌。此令牌主要用于从 OIDC /oidc/userinfo
端点检索用户配置信息。收到带有不透明访问令牌的请求后,授权服务器检查其内部存储以检索相关的授权信息,并验证令牌的有效性,然后响应用户资料详细信息。
- 刷新令牌交换:
刷新令牌设计为仅在客户端和授权服务器之间交换,无需与资源提供者共享。因此,刷新令牌通常作为不透明令牌签发。当当前访问令牌过期时,客户端可以使用不透明刷新令牌获取新的访问令牌,确保连续访问而不必重新认证用户。
JWTs
- ID 令牌:
在 OIDC 中,ID 令牌是包含用户信息的 JWT,用于认证用户。通常与访问令牌一起签发,ID 令牌允许客户端验证用户的身份。例如:
// 解码后的 ID 令牌载荷
{
"iss": "<https://logto.io>",
"sub": "1234567890",
"aud": "client_id",
"exp": 1630368000,
"name": "John Doe",
"email": "[email protected]",
"picture": "<https://example.com/johndoe.jpg>"
}
客户端可以验证 ID 令牌以确保用户身份,并提取用户信息用于个性化或授权目的。ID 令牌仅供一次性使用,不应用于 API 资源的授权。
- API 资源访问(使用访问令牌):
当客户端请求具有特定资源指示符的访问令牌时,授权服务器会发布用于访问该资源的 JWT 访问令牌。JWT 包含资源提供者可以用于授权客户端访问的声明。例如:
// 解码后的 JWT 访问令牌载荷
{
"iss": "<https://dev.logto.app>",
"sub": "1234567890",
"aud": "<https://api.example.com>",
"scope": "read write",
"exp": 1630368000
}
资源提供者可以通过检查声明来验证请求:
iss
: 确保令牌由受信任的授权服务器颁发。sub
: 确认与令牌相关联的用户。aud
: 确保令牌用于特定资源。scope
: 验证授予用户的权限。