Article: Server-Side DPoP and Token Exchange in Spring Security 7

TL;DR · AI 摘要
Spring Security 7整合DPoP和令牌交换功能,迁移需调整配置,影响服务间通信安全架构。
核心要点
- Spring Authorization Server 1.5.x升级至7.1需显式调用.dPoP()方法启用DPoP验证
- RFC 8693令牌交换将服务账号切换标准化为标准授权模式
- 分布式集群需替换默认内存jti重放缓存实现
结构提纲
按章节快速跳转。
- §迁移影响
Spring Authorization Server整合至Spring Security 7导致迁移策略变化
Spring Security 7.1要求显式配置.dPoP()方法启用证明持有验证
- ›令牌交换
RFC 8693标准实现服务账号切换,支持act声明链式委托
- ·配置调整
需替换默认内存jti缓存为分布式实现以支持集群部署
- ›安全影响
组合使用DPoP和令牌交换可构建更安全的服务间通信架构
思维导图
用一张图看清主题之间的关系。
查看大纲文本(无障碍 / 无 JS 友好)
- Spring Security 7安全特性
- DPoP实现
- 需显式.dPoP()配置
- JWK指纹绑定cnf.jkt
- 令牌交换
- RFC 8693标准
- act声明链式委托
- 迁移注意事项
- 配置变更
- 分布式缓存替换
金句 / Highlights
值得收藏与分享的关键句。
Spring Authorization Server 1.5.x升级至7.1需显式调用.dPoP()方法启用DPoP验证
RFC 8693令牌交换将服务账号切换标准化为标准授权模式,包含subject_token和actor_token语义
组合使用DPoP和令牌交换可构建更安全的服务间通信架构,但安全性取决于交换策略实现
Spring Security 7 中的服务器端 DPoP 和令牌交换 - InfoQ
InfoQ 首页 文章 Spring Security 7 中的服务器端 DPoP 和令牌交换
Java
扩展 OpenTelemetry:管理收集器、配置和成本(网络研讨会 11 月 19 日)
Spring Security 7 中的服务器端 DPoP 和令牌交换
2026 年 10 月 15 日 19 分钟阅读
作者:
- Dhruv Agnihotri
审阅者:
- A N M Bazlur Rahman
#### 关注我们
YouTube
232K 粉丝
26K 粉丝
新
RSS
19K 读者
X
57.1k 粉丝
21K 点赞
Bluesky
收听本文 -
0:00
音频准备播放
您的浏览器不支持音频元素。
正常
1.25x
1.5x
喜欢
新下拉阅读列表
- 阅读列表
关键要点
- Spring Authorization Server 现在位于 Spring Security 7 中,使用相同的 Maven 坐标,因此从 1.5.x 版本迁移只需版本升级,而无需更换构件。
- 在 Spring Security 6.5 和 Spring Authorization Server 1.5 的组合中,Proof of Possession(DPoP)(RFC 9449)已能实现端到端验证。Spring Security 7.1 的真正变化是资源服务器验证不再自动执行,因此依赖旧版自动行为的应用在升级后会静默停止执行 DPoP,直到添加新的 .dPoP() 调用。
- Spring 会自行绑定 cnf.jkt 的 JWK 指纹和公共客户端的刷新令牌,留给身份提供商的两个真实决策是:nonce 策略以及是否需要将默认的内存单节点实现的 jti 重放缓存替换为集群分布式实现。
- RFC 8693 令牌交换将许多身份提供商(IdP)仍在使用的定制“服务账户交换”转变为标准授权模式,引入 subject_token 和 actor_token 语义以及记录委托链的
act声明。
- 这两个功能结合使用时效果更佳,因为通过 DPoP 绑定的 subject token 交换为受众受限的下游令牌,正是服务间通信和智能体架构持续追求的模式,但这种组合的安全性取决于您实现的交换策略和令牌自定义逻辑。
您的内部身份提供商已运行 Spring Authorization Server 1.x 版本两年。升级到 Spring Boot 4 在纸面上看起来是常规操作。您升级 Bill of Materials(BOM),修复弃用项,然后发布。但打开迁移指南后,您会发现依赖的授权服务器不再是独立项目。它在您使用 1.x 版本期间已迁移到 Spring Security 7.0。在当前的 7.1 版本中,与 Spring Boot 4.1 配合,您之前推迟的两个 OAuth 2.1 功能——发送者约束令牌和真正的委托授权模式——已整合到同一模块中,随 Spring 官方发布(GA)版本一同等待启用。
这是 Spring Boot 4 迁移指南中被跳过的部分。指南通常涵盖 Jackson 3、lambda DSL 和包迁移,然后在 OAuth 2.0 处停止。认证层的收益体现在 DPoP(RFC 9449)和 Token Exchange(RFC 8693)这两个功能上,它们首次在官方组件中实现生产就绪状态,这也是升级对运行自有 IdP 的用户真正有价值的地方。本文是服务器端的实践指南,涵盖已整合的内容、启用 DPoP 后的变化、端到端的 RFC 8693 交换流程,以及这两个功能为何值得同时启用。文章假设读者已运行 OAuth 2.0/OIDC 服务器并了解协议,不会从头解释 OAuth。
Spring Boot 4 整合内容
Spring 2025 年 11 月的发布列车分三步完成:Spring Framework 7.0 于 11 月 13 日发布,Spring Security 7.0 于 11 月 17 日发布,Spring Boot 4.0 于 11 月 20 日发布。发布说明以模块化、JSpecify 空安全、API 版本控制和 Jackson 3 基线为核心。OAuth 服务器功能位于这些模块之下,相关变更早在两个月前就已宣布。
2025 年 9 月 11 日,Spring 团队宣布自 2020 年作为独立项目发布、自 2022 年 11 月 GA 的 Spring 授权服务器将整合到 Spring Security 7.0 中。独立的 1.5.x 分支是最后一个独立开发版本,后续开发已纳入 Spring Security。Spring Security 于 2026 年 6 月发布 7.1.0,配套 Spring Boot 4.1,随后在 2026 年 8 月发布 7.1.1,当前坐标为 org.springframework.security:spring-security-oauth2-authorization-server:7.1.1。整合本身发生在 7.0 版本。本文示例明确针对 7.1.1,因为本文涉及的版本特定细节(资源服务器 .dPoP() 选项和可插拔 jti 重放缓存)均在此版本实现:.dPoP() 选项(7.1.0 新增)和可插拔 jti 重放缓存(7.1.1 新增)。每个功能在文中出现时都会特别说明。对于一直将“授权服务器”视为独立依赖并遵循独立发布节奏的团队来说,整合是核心亮点。现在只有一个安全模块、一个版本和一组发布说明需要跟踪。
此处有一个细微但关键的细节需要注意,因为这是人们在此迁移中最常见的误解。Maven 坐标没有变化。独立项目早已发布在 org.springframework.security:spring-security-oauth2-authorization-server 下,而 Spring Security 7 中的合并模块使用相同的 groupId 和 artifactId。唯一的变化是版本从 1.5.x 线迁移到 Spring Security 版本线(7.0.x,现为 7.1.x)。如果你在某处看到需要重命名 spring-authorization-server 艺术品的说法,这个艺术品从未存在过。唯一需要重命名的情况是:如果你通过 Boot starter 引入授权服务器而非直接使用库。在 Boot 4 中,starter 增加了 security- 前缀,因此 spring-boot-starter-oauth2-authorization-server 已弃用,推荐使用 spring-boot-starter-security-oauth2-authorization-server,两者在 4.1.1 中仍同时发布。除此之外,迁移仅涉及版本升级和相同的包迁移。Jackson 的迁移影响所有 Spring Security 7 的使用者。这不是坐标交换,而是版本线迁移。
Xml
<!-- 之前:基于1.5.x版本的独立Spring Authorization Server -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
<version>1.5.7</version>
</dependency>
<!-- 之后:相同坐标,版本号与Spring Security(当前主线)保持同步 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
<version>7.1.1</version>
</dependency>迁移到7.x版本后,你将继承一个模块,其中DPoP令牌颁发、刷新令牌DPoP绑定和Token Exchange功能已与Spring Security 6.5中达到GA(通用可用)状态的资源服务器DPoP支持共存。在此整合之前,要同时获得这些功能需要将特定版本的独立授权服务器与特定版本的Spring Security绑定,并希望版本矩阵能够匹配。现在只需使用一个版本号即可。
图1. Spring Boot 4整合方案。(来源:作者原创)
如图1所示,独立的Spring Authorization Server(1.5.x,上一代版本)在相同Maven坐标下被整合进Spring Security 7.x主线,将DPoP和Token Exchange功能合并到单一模块中。
一次性支付的迁移成本
在任何新功能之前,Spring Security 7引入了一项基础迁移成本,且大部分与OAuth无关:HttpSecurity构建器中的and()方法已被移除,Jackson 3取代了Jackson 2,以及几个JSON Web Token(JWT)解码器的默认配置发生了变化。Spring官方的迁移指南及其专门针对OAuth 2.0的页面详细介绍了这些变更机制,具体操作步骤见下文策略中的第2至第4项。
该指南未明确说明两点,因为它们仅适用于运行授权服务器,而非一般的Spring Security应用。首先,typ头的变更并非放宽验证规则,而是Spring最终自动实现了6.5版本准备步骤中要求手动添加的功能:validateTypes(false)会关闭Nimbus自身的typ检查,使你可以自行注册JwtTypeValidator.jwt()。在7.0版本中,这一过程将自动完成。实际审计点比"我的令牌还能验证吗?"更具体。JwtTypeValidator.jwt()要求typ必须为JWT或缺失,因此如果你的服务器颁发或接受的令牌使用了其他typ值(例如RFC 9068定义的at+jwt访问令牌类型),默认情况下将无法通过验证,除非你注册了一个允许该类型的验证器。其次,如果你使用Spring的Jackson模块将OAuth2Authorization对象持久化到数据库中,该注册需要从Jackson 2的SecurityJackson2Modules迁移至Jackson 3的SecurityJacksonModules,且新模块位于org.springframework.security.jackson包下,否则之前持久化的授权状态将无法反序列化。
对于已跟进Spring Security 6.5弃用项的团队,基础迁移可大致规划为在接触任何新的OAuth 2.1功能前的一次冲刺(sprint)工作量。而对于版本滞后较多的团队,应将基础迁移与功能采用视为两个独立的工作任务。
DPoP,端到端
DPoP(RFC 9449)将访问令牌绑定到客户端持有的密钥。客户端生成一个非对称密钥对,每次请求时都会对一个短期存在的证明JWT进行签名。授权服务器在令牌端点验证该证明,并将发放的访问令牌绑定到证明的公钥。资源服务器随后会拒绝接受该令牌,除非它附带来自相同密钥的新鲜证明。没有私钥的情况下,被盗的令牌将无法使用。这就是整个机制的核心,也是本文浏览器端配套文章的主题,该文章讨论了浏览器如何安全存储密钥这一难题。此处关注的是另一端的处理逻辑,即当证明到达服务器时服务器应如何处理。
证明本身的定义见于RFC 9449 §4.2。头部包含typ: dpop+jwt、非对称算法以及以jwk格式内联的客户端公钥。负载部分通过htm(HTTP方法)、htu(目标URI,去除查询参数和片段)、iat(时间戳)和唯一jti(JWT ID)将证明绑定到请求。当证明随访问令牌一起发送到资源服务器时,还会携带该令牌的base64url SHA-256哈希值。
图2. DPoP循环(服务器端)。授权服务器将令牌绑定到客户端的密钥,资源服务器在没有匹配证明时会拒绝该令牌。(来源:作者原创。)
颁发:默认配置中需要你自行决定的两个事项
在授权服务器端,Spring会为你配置证明验证机制。DPoPProofVerifier和配套的DPoPProofJwtDecoderFactory执行RFC 9449 §4.3的验证逻辑:格式正确的JWT、typ字段为dpop+jwt、非none的非对称算法、能够通过内嵌jwk验证的签名,以及htm/htu与令牌请求匹配。有两个看似需要决策的事项实际上并非如此。真正需要你自行处理的是:(1)是否需要自行构建nonce挑战(如果需要的话),(2)重放缓存的覆盖范围。
cnf.jkt绑定由框架自动处理。访问令牌必须包含cnf声明,其中jkt字段是证明密钥的base64url编码JWK SHA-256指纹(RFC 7638)。当启用DPoP时,Spring会自动填充该密钥;唯一需要注意的是避免自定义令牌处理器意外覆盖cnf字段。资源服务器后续会通过指纹进行比对,而非公钥或证书哈希。
JSON
{
"sub": "service-a",
"aud": "https://orders.internal",
"cnf": {
"jkt": "0ZcOCORZNYy-DWpqq30jZyJGHTN0d2HglBV3uiguA4I"
}
}首先考虑nonce挑战机制,Spring完全未实现该功能。RFC 9449 §8允许授权服务器在证明中要求客户端包含服务器生成的nonce,这可以防止客户端预先生成证明;资源服务器在§9中有对应的平行机制。当服务器要求客户端未提供的nonce时,会返回错误:use_dpop_nonce,并携带DPoP-Nonce响应头,在授权服务器返回400错误,在资源服务器返回401错误。截至Spring Security 7.1.1版本,Spring的DPoP代码在两端均未实现这些功能:既不会发出DPoP-Nonce响应头,也不会处理use_dpop_nonce错误,也没有验证器检查证明中的可选nonce声明。nonce挑战在RFC中是建议(SHOULD)而非强制(MUST)要求,这可能是其未被实现的原因。但如果你的安全模型需要该功能,就必须自行构建完整机制:生成nonce、返回挑战响应、存储已发放的nonce,并验证声明与存储内容是否匹配。
为你处理的另一项内容:公共客户端的刷新令牌绑定
RFC 9449 §5 规定,当公共客户端在令牌端点出示 DPoP 证明时,其获得的刷新令牌必须绑定到该密钥,后续的刷新操作必须证明相同的密钥。Spring Authorization Server 从 1.5.0-RC1 版本(2025 年 4 月)开始自动添加此密钥,并延续到 7.x 系列;无需任何开关配置。机密客户端则依赖其现有的客户端凭证绑定,因此此机制对单页应用(SPAs)和原生应用最为重要。
第二,还有重放缓存。RFC 9449 §11.1 要求在可接受的时间窗口内,若某证明的 jti 已经出现过,必须拒绝该证明。在 Spring Security 7.0.x 和 7.1.0 中,该检查是硬编码实现的:DPoPProofJwtDecoderFactory 内部埋入了一个固定大小的内存缓存(容量为 1000 条目),且无法替换。自 Spring Security 6.5.12 / 7.1.1 版本起,改用可插拔的 DPoPProofReplayValidator,默认配置使用其自身的 InMemoryCache(容量为 10 万条目,按密钥限制)。该默认实现的 Javadoc 明确指出,它“仅适用于单节点部署”,并指出 Redis 和 Hazelcast 是集群部署所需的分布式缓存类型。
资源服务器现在默认支持验证
Spring 7 在资源服务器端移除了最多的自定义代码。绑定 DPoP 的访问令牌验证功能通过 Spring Security 6.5.0 的里程碑版本引入,并在 6.5.0(2025 年 5 月)正式发布。此处有一个版本细节需要注意。在 Spring Security 7.0 中,只要 DPoP 相关类在类路径上,就会自动启用该功能,无需任何 DSL 钩子。Spring Security 7.1 在资源服务器 DSL 中新增了显式的 dPoP(Customizer) 方法(@since 7.1),使该功能变为可选且可定制;在 7.1 中,需调用 .dPoP() 来启用。这是升级后真正的行为变更,而不仅仅是新增选项:任何依赖 6.5 或 7.0 自动启用验证的应用(包括完全基于 Spring Boot 默认资源服务器自动配置构建的应用,该配置也从未调用新钩子)在升级到 7.1 后,只要未显式添加 .dPoP(),就会立即停止强制 DPoP 验证。绑定 DPoP 的令牌使用 DPoP 认证方案,而非 Bearer。将其作为 Bearer 发送是一种常见但错误的快捷方式。Spring 的 DPoPAuthenticationProvider 负责处理验证。
一个需要注意的命名陷阱值得提醒,因为它可能在代码审查中引发问题。资源服务器提供者名为 DPoPAuthenticationProvider,没有 OAuth2 前缀,与下一节的 OAuth2TokenExchangeAuthenticationProvider 不同。转换器和提供者也位于不同包中。DPoPAuthenticationConverter 位于 ...resource.web.authentication,而 DPoPAuthenticationProvider 和 DPoPAuthenticationToken 位于 ...resource.authentication,不带 web 后缀。
Java
// 资源服务器:接受绑定 DPoP 的访问令牌。
@Bean
public SecurityFilterChain resourceServer(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated())
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(Customizer.withDefaults()) // 验证访问令牌
.dPoP(Customizer.withDefaults()) // 验证证明:签名、htm/htu、ath、cnf.jkt
);
return http.build();
}配置 .dPoP() 后,资源服务器会验证证明签名,将 htm/htu 与传入请求进行比对,检查证明的路径是否等于所展示令牌的哈希值,并确认证明密钥的指纹是否等于令牌的 cnf.jkt。在 Spring Security 6.5 之前,这需要手动编写过滤器代码。现在只需在 DSL 中一行代码即可实现。
无需自定义管道的令牌交换
大多数企业内部的 IdP 都会实现某种形式的"服务账户交换"。当服务 A 需要代表用户调用服务 B 时,它会获取第二组凭证,通过侧通道生成新令牌,或者将用户的令牌传递给下游服务,并希望受众检查较为宽松。RFC 8693 用标准授权码替代了这种做法。
令牌交换功能已集成到 Spring Authorization Server 1.3.0-M3(2024 年 3 月发布)中,并包含在 7.x 模块中。授权码类型为 urn:ietf:params:oauth:grant-type:token-exchange。请求包含 subject_token(代表请求方的令牌)和可选的 actor_token(代表操作方的令牌),以及调用方期望的受众、资源和作用域。
Http
POST /oauth2/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
DPoP: eyJ... # 边缘服务的证明,使颁发的令牌重新绑定到其密钥
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token=eyJ... # 传入的用户令牌,代表调用方
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&actor_token=eyJ... # 边缘服务自身的令牌;这会产生 act 声明
&actor_token_type=urn:ietf:params:oauth:token-type:access_token
&audience=https://orders.internal
&scope=orders:readRFC 8693 第1.1节区分的两个语义对令牌交换最为关键。模拟(Impersonation)会颁发一个看似是主体的令牌,操作方身份会消失。委托(Delegation)则保留两个身份,颁发的令牌同时包含主体和 act(操作方)声明。使用哪种方式取决于单一参数。仅发送 subject_token 时,Spring 的 OAuth2TokenExchangeAuthenticationProvider 会颁发不含 act 声明的模拟令牌。如果同时发送 actor_token,提供商会生成委托令牌,从 actor_token 而非认证客户端中推导出 act 声明。对于后续需要审计的内容和服务间调用,委托功能正是你所需。
RFC 8693 第4.1节通过嵌套 act 声明表示多跳链,最外层是当前操作方,每个嵌套层代表先前操作方。嵌套条目来自主体令牌。如果网关之前的交换已在当前边缘服务作为 subject_token 提供的令牌上留下了 act 声明,Spring 会将先前操作方作为嵌套条目传递。因此在这个示例中,边缘服务(actor token 的拥有者)成为当前操作方,网关服务则作为先前操作方嵌套在其下。
JSON
{
"sub": "user-123",
"aud": "https://orders.internal",
"scope": "orders:read",
"act": {
"sub": "edge-service",
"act": {
"sub": "gateway-service"
}
}
}在 Spring 端,令牌交换授权由 OAuth2TokenExchangeAuthenticationProvider 处理,该组件通过授权服务器配置器注册。该授权类型默认不会为每个客户端启用,调用客户端的 RegisteredClient 必须显式声明该授权类型,否则提供者会以 unsupported_grant_type 拒绝请求。
Java
RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("edge-service")
// ... 客户端认证、作用域 ...
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)
.build();act 声明本身是框架的职责,它根据上述描述的 actor 令牌构建,因此你无需手动组装。你需要完成的是通过移除作用域或切换受众来缩小已发放令牌的范围,这由 OAuth2TokenCustomizer<JwtEncodingContext> 类型的 Bean 处理,这与任何声明定制所需的钩子相同。
Java
@Bean
public OAuth2TokenCustomizer<JwtEncodingContext> tokenCustomizer() {
return context -> {
if (AuthorizationGrantType.TOKEN_EXCHANGE.equals(
context.getAuthorizationGrantType())) {
// 缩小受众并修剪交换令牌的作用域
context.getClaims().audience(List.of("https://orders.internal"));
context.getClaims().claim("scope", "orders:read");
}
};
}有一个原则需要牢记:交换过程是权限的缩减,因此绝不能扩大权限。如果令牌交换端点能够访问比主体令牌所拥有的更大作用域,这属于权限提升漏洞而非功能特性。
需要同时使用 DPoP 和令牌交换的模式
这两个特性单独使用时都有价值。当组合使用时,它们解决了各自无法单独解决的问题:在服务链中传播受限权限,而无需为每个中间节点提供可重放的令牌。
设想一个边缘服务从客户端接收到一个绑定 DPoP 的访问令牌。它需要代表用户调用订单服务,但不应直接转发用户的令牌。该令牌的受众是边缘服务,直接转发一个 bearer 副本会让订单服务获得可重放的凭证。相反,边缘服务执行令牌交换:它将用户的令牌作为 subject_token,自己的令牌作为 actor_token,并在交换请求中附加 DPoP 证明,然后请求一个作用域为 orders:read、受众为 https://orders.internal 的令牌。授权服务器发放一个范围缩小的令牌,从该 actor 令牌中记录边缘服务到 act 声明,并因为边缘服务发送了证明,通过 cnf.jkt 将新令牌绑定到边缘服务自身的密钥。
图3:绑定 DPoP 的令牌交换为一个范围缩小、重新绑定的下游令牌。作用域缩小、受众切换、actor 被记录,每个中间节点都受到发送方约束。(来源:作者创作)
结果是具体的:订单服务接收到一个可验证的令牌,该令牌严格限制为实际通信的调用方,作用域精确匹配调用需求,并保留了委托关系的审计轨迹。每个中间节点都不持有可重放的凭证用于未调用的服务。这种模式正是现代服务网格和智能系统反复尝试通过临时头部传递实现的,而 RFC 8693 和 RFC 9449 是标准跟踪版本,Spring Security 7 是首个官方整合这两个特性的 Spring 实现。
有一个真正的注意事项。范围缩小是需要你来强制执行的。Spring 为你提供了授权和自定义钩子,但它不会阻止你编写一个扩大范围或跳过受众切换的交换。该模式的安全性取决于你编写的 OAuth2TokenCustomizer。这段代码值得拥有自己的测试。
迁移策略
大多数团队可以从两个起点开始。第一个是基于 Spring Boot 4.1 的全新项目。新的身份提供商从 7.x 模块开始,DPoP 和 Token Exchange 从第一天起就可用。工作内容是配置,而非迁移:注册客户端、决定你的 nonce 策略、编写缩小范围的自定义器,并在每个资源服务器上启用 .dPoP()。从第一次提交开始,就将 OAuth2TokenCustomizer 视为关键安全代码。
第二个起点是现有的独立授权服务器 1.5.x。这是一个你已经在使用的坐标版本的升级,而不是重新平台化。步骤如下:
- 迁移到 Spring Boot 4.1 / Spring Security 7.1 BOM。将 spring-security-oauth2-authorization-server 从 1.5.x 升级到 7.1.x,保持相同的 groupId 和 artifactId。
- 将任何剩余的 and() 风格过滤器链转换为 lambda DSL。
- 审计 JwtDecoder 配置中的 validateTypes(false),确认新的 JwtTypeValidator 默认值是否接受你的令牌。
- 更新 Jackson 模块注册,当你持久化授权状态时,将 SecurityJackson2Modules 改为 SecurityJacksonModules。
- 仅在此之后,作为单独的更改,启用新客户端注册的 DPoP 并启动 token-exchange 授权,前提是基线迁移已通过。
生产环境的部署有其自身的决策。将功能标志转换为部署的决策。在切换过程中保持颁发者 URL 稳定。在授权服务器元数据中宣传 DPoP 和 token-exchange 授权,以便客户端可以发现它们。在开始颁发 DPoP 绑定令牌之前,先部署资源服务器的 .dPoP() 支持,确保验证器在第一个绑定令牌到达时已就绪。计划一个双栈窗口期,让 bearer 和 DPoP 绑定令牌共存,以便客户端迁移。按客户端注册启用 DPoP 而非全局启用,这样现有舰队可以继续工作,而新注册可以自愿加入。
元数据是让客户端发现所有这些功能的关键。授权服务器的 /.well-known/oauth-authorization-server 文档应宣传这两种能力:它接受的 DPoP 签名算法,以及其 grant-type 列表中的 token-exchange 授权。
JSON
{
"issuer": "https://idp.internal",
"grant_types_supported": [
"authorization_code",
"refresh_token",
"urn:ietf:params:oauth:grant-type:token-exchange"
],
"dpop_signing_alg_values_supported": ["ES256", "RS256"]
}接下来会发生什么
特别关注 nonce 间隙问题。RFC 标记为 SHOULD 而非 MUST,截至本文撰写时,Spring Security 的问题跟踪器中没有关于此功能的开放请求,因此想要此功能的身份提供商需要独自构建它,并应为此做好计划,而不是等待未来的版本发布。
更持久的趋势位于这一发布之上。跨信任边界的多跳服务调用和日益增长的自主代理链需要密钥持有证明和经过审计的、逐步缩小的委托链,而不仅仅是承载令牌和范围检查。RFC 8693 的嵌套 act 声明已经建模了这种链。随着这种架构的普及,本文讨论的 DPoP 绑定加令牌交换组合,看起来更像是身份提供商的默认预期,而不是 Spring Security 7 的特定功能组合。
参考文献
- RFC 9449,OAuth 2.0 证明持有(DPoP),IETF,2023 年 9 月。
- RFC 8693,OAuth 2.0 令牌交换,IETF,2020 年 1 月。
- RFC 7638,JSON Web 密钥(JWK)指纹,IETF,2015 年 9 月。
- "Spring Authorization Server 正在转向 Spring Security",Spring 博客,2025 年 9 月 11 日。
- "Spring Framework 7.0 正式发布",Spring 博客,2025 年 11 月 13 日。
- "Spring Boot 4.0.0 现已发布",Spring 博客,2025 年 11 月 20 日。
- Spring Security 7.0.0 发布,GitHub,2025 年 11 月 17 日。
- Spring Security 参考文档,OAuth 2.0 DPoP 绑定访问令牌。
- Spring Security 问题 #16940 / PR #17202,"添加 dpop 自定义支持"(dPoP(Customizer),@since 7.1)。
- "DPoP 存储悖论:为什么基于浏览器的持有证明仍然是未解决的问题",InfoQ,2026 年 4 月(配套文章)。
(所有 URL 于 2026 年 10 月访问。)
作者信息主容器
关于作者
章节标题
每个作者的主容器
#### Dhruv Agnihotri
显示更多
显示更少
#### 本内容属于 Java 主题
##### 相关主题:
- 开发
- 架构与设计
- Spring Security
- Spring
- Java
- 相关编辑
- 相关赞助商《实用代理编程实践手册》
- 相关赞助商:大规模自动化代码转换。修复漏洞、迁移框架、现代化所有仓库中的遗留代码。立即免费试用 Moderne。
InfoQ 新闻通讯
每周五发送 InfoQ 最新内容摘要。加入超过 25 万名高级开发者的社区。查看示例
我们保护您的隐私。