两种令牌格式
概述
在 OAuth2 体系中认证通过后返回的令牌信息分为两大类:不透明令牌(Opaque token) 和 JSON Web Token (JWT) 。
[1]什么是不透明令牌
不透明令牌是一种访问令牌,正如其名字所示,对于客户端或任何外部方是不可透明的。这意味着令牌本身不携带关于用户或授权的任何可读信息。
当你收到不透明令牌时,它通常显示为一个看似随机的字符字符串,尝试解码它将不会产生有意义的数据。
这里有一个不透明令牌的例子:
M-oxIny1RfaFbmjMX54L8Pl-KQEPeQvF6awzjWFA3iq
由于令牌的实际内容仅为发行它的授权服务器所知,因此,要验证不透明令牌,客户端必须将其返回给服务器,然后服务器验证其真实性并确定相关的权限。这种方法确保敏感信息保持隐藏,提供了一层额外的安全性,但也需要额外的服务器请求来验证令牌。
优点
- 安全:不透明令牌不暴露任何敏感信息给客户端。令牌的内容仅为授权服务器所知。
- 可撤消:由于令牌存储在服务器上,且唯一验证方法是通过授权服务器上的
Introspection
端点,服务器可以轻松撤消令牌以防止未经授权的访问。 - 小尺寸:不透明令牌通常比JWT短小,这对于性能和存储考察有利。
缺点
- 有状态:不透明令牌要求授权服务器维持状态以验证令牌,这可能引入附加的复杂性和开销。
- 性能:验证令牌所需的额外服务器请求可能会影响性能,特别是在高流量场景中。
[2]什么是 JWT
与不透明令牌相反,JWT(JSON Web Token)是一种自包含的、无状态的令牌,承载信息以结构化和可读格式表达。
JWT 由三部分组成:header
,payload
,和signature
,每个部分都以Base64
编码。
这里有一个 JWT 的例子:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
header
:包含有关令牌类型和用于签名的算法的信息。例如,{"alg": "HS256", "typ": "JWT"}
。payload
:区段包含声明—关于用户或授权的信息片段,如用户ID、到期时间和范围。由于这些数据是编码的但未加密,任何拥有令牌的人都可以解码以查看声明,但无法在不使签名失效的情况下更改它。根据规范和授权服务器配置,可以在payload
中包括各种声明,这赋予令牌其自包含的特性。例如,{"sub": "1234567890", "name": "John Doe", "iat": 1516239022}
。signature
:是通过使用指定的算法结合header
,payload
和密钥来生成的。这个签名用于验证令牌的完整性并确保它没有被篡改。
由于它们可以由客户端或任何服务在本地验证,而无需与授权服务器互动,JWT得到了广泛使用。这使得JWT对于分布式系统特别高效,其中多个服务可能需要独立验证令牌的真伪。然而,这种便利也带来了确保令牌声明未过度暴露的责任,因为它们对任何有权访问令牌的人都是可见的。此外,JWT 通常是短命的,到期时间包含在令牌的声明中,以确保令牌不会无限期有效。
优点
- 无状态:JWT 是自包含的,无需服务器端状态即可验证。
- 跨服务兼容性:JWT 可以轻松在不同服务之间共享和验证,使其对于分布式系统理想。
- 可扩展:JWT 的
payload
可以包含自定义声明,允许灵活的授权和信息共享。 - 标准:JWT 令牌遵循一个良好定义的标准(RFC 7519),使其广泛支持和可互操作。
缺点
- 暴露:JWT 中的声明对任何拥有该令牌的人都是可见的,因此不应在
payload
中包括敏感信息。 - 大尺寸:由于携带附加的信息,JWT 可以比不透明令牌更大,这可以影响性能和存储考察。JWT 令牌中的声明应保持在最小以减少令牌大小。
- 撤消复杂性:由于JWT是无状态的,通常在一段时间内有效,且没有内置的令牌撤消机制,这意味着被危及的令牌可能会在失效前一直有效。
重要
实际上,JWT 令牌是一个完整的JSON对象,使用 base64 编码
需要明确一点:在不借助外力的情况下,让 JWT 失效的唯一途径就是等 token 自己过期,无法做到主动让 JWT 失效。非要让 JWT 有主动失效的功能只能借助外力,即在服务端存储 JWT 的状态,在请求时添加判断逻辑,这个与 JWT 的无状态化、去中心化特性是矛盾的。
[3]不透明令牌验证
不透明访问令牌通过将其发送回授权服务器进行验证。授权服务器维护已发行令牌的状态,并可以根据其内部存储确定令牌的有效性。
- 客户端向授权服务器请求访问令牌。
- 授权服务器颁发不透明令牌。
- 客户端在请求头中发送带有不透明令牌的资源访问请求。
- 资源提供者向授权服务器发送令牌内省请求以验证令牌。
- 授权服务器响应令牌信息。
[4]JWT 访问令牌验证(离线)
JWT 访问令牌可以由客户端或任何拥有令牌公钥的服务离线验证。
- 资源提供者预先从 OpenID Connect (OIDC) 发现 (Discovery) 获取授权服务器的公钥。公钥用于验证令牌的签名并确保其完整性。
- 客户端向授权服务器请求访问令牌。
- 授权服务器颁发 JWT 令牌。
- 客户端在请求头中发送带有 JWT 令牌的资源访问请求。
- 资源提供者使用从授权服务器获得的公钥解码并验证 JWT 令牌。
- 资源提供者根据令牌的有效性授予访问权限。
[二]Dante Cloud 中的令牌
Dante Cloud 完全遵循 OAuth2.1 开发,所以支持 JWT 和 Opaque 两种 Token。在实际应用中,根据使用场景的不同 JWT 和 Opaque 两种 Token 应用也会有所区别。
[1]AccessToken 和 RefreshToken
**访问令牌(Access Token)和刷新令牌(Refresh Token)**是保障用户安全访问资源的关键组件。它们在OAuth2授权框架中使用,以管理用户的登录状态和访问权限。
- 访问令牌(Access Token):访问令牌是一个短期的凭证,用于在用户和资源服务器之间进行身份验证。当用户成功登录后,授权服务器会发放访问令牌给用户,用户随后可以使用此令牌来请求资源服务器上的受保护资源。访问令牌具有有限的有效期,这是为了减少令牌被盗用的风险。一旦访问令牌过期,用户就无法再使用它来访问资源。
- 刷新令牌(Refresh Token):刷新令牌用于在访问令牌过期时获取新的访问令牌,而无需用户重新登录。这提供了更好的用户体验,因为用户不需要频繁地输入登录凭证。刷新令牌通常具有比访问令牌更长的有效期,并且只能用于从授权服务器获取新的访问令牌。
Dante Cloud 中 AccessToken 和 RefreshToken 均支持 JWT 和 Opaque 两种 Token 类型,可以通过配置进行变更和指定。
变更 Token 类型的方法,主要有以下两步;
第一步修改系统配置,主要在 UAA 服务中进行修改
herodotus:
oauth2:
authorization:
token-format: jwt
更多配置,参见:【资源服务】
第二步修改数据表中的数据
找到 oauth_application
和 oauth2_registered_client
手动修改其中的数据:
- 修改
oauth_application
表中access_token_format
字段的值。1
表示为 Opaque Token,2
表示为 JWT Token。 - 修改
oauth2_registered_client
表中token_settings
字段中,JSON 属性settings.token.access-token-format
对应的值。reference
表示为 Opaque Token,self-contained
表示为 JWT Token。
重要
为什么采用这么“笨拙”的方式切换Token?
这个问题主要是受到使用的底层基础组件的局限所致。
Token 类型在系统中主要用于两个地方:授权服务器对Token的签发和资源服务器对Token的验证。
- 授权服务器对Token的签发:是由
Spring Authorization Server
组件负责。通过修改 TokenSettings 中的OAccess-token-format
就可以变更所签发的 Token 类型。这个是没有任何问题的。 - 资源服务器对Token的验证:是由
Spring Security
组件负责。根据你配置的 JWT 或 Opaque 策略对 Token 进行校验,但是 JWT 或 Opaque 相关配置不能同时存在
这就导致授权服务器对Token的签发和资源服务器对Token的验证出现了矛盾点。参见 ISSUE:#1211
重要
正是因为 JWT Token 和 Opaque Token 本质上的区别,所以在 Dante Cloud 中,如果您选择使用 JWT Token,那么很多合规性功能将无法使用。因此,Dante Cloud 中默认使用的是 Opaque Token。
[2]IdToken
在 OIDC 中,ID Token 是一个包含用户信息的 JWT,用于认证用户。通常与访问令牌一起颁发,ID 令牌允许客户端验证用户的身份。例如:
// ID 令牌的解码有效负载
{
"iss": "<https://auth.wiki>",
"sub": "1234567890",
"aud": "client_id",
"exp": 1630368000,
"name": "John Doe",
"email": "john.doe@mail.com",
"picture": "<https://example.com/johndoe.jpg>"
}
客户端可以验证 ID 令牌以确保用户的身份,并提取用户信息以用于个性化或授权目的。ID 令牌仅供一次性使用,不应用于 API 资源授权。
重要
IdToken 默认为 JWT 格式 Token,不支持 Opaque 类型。而且从使用用途和使用便捷型角度看,IdToken 也无需支持 Opaque。