如何在Spring Cloud Gateway中刷新Id Token?已实现自定义TokenRelay过滤器
ID Token 刷新功能实现思路
针对你已实现CustomTokenRelayGatewayFilterFactory且现有AccessToken刷新逻辑正常的场景,要扩展ID Token刷新功能,可按以下步骤落地:
1. 确认OIDC提供商支持ID Token刷新
首先要验证你的身份提供商(如Keycloak、Auth0、Okta等)是否支持通过refresh_token获取新的ID Token:
- 查阅提供商文档,确认使用
refresh_token请求令牌时,响应体是否会返回id_token字段; - 部分提供商要求刷新请求必须携带
openidscope,否则不会返回ID Token,需提前确认。
2. 修改刷新请求参数,确保携带必要Scope
现有刷新请求的refreshTokenBody方法需要补充scope参数,保证包含openid(如果初始授权时包含该scope):
private static MultiValueMap<String, String> refreshTokenBody(final String refreshToken, Collection<String> scopes) { final MultiValueMap<String, String> body = new LinkedMultiValueMap<>(); body.add(GRANT_TYPE_KEY, AuthorizationGrantType.REFRESH_TOKEN.getValue()); body.add(REFRESH_TOKEN_KEY, refreshToken); // 携带原有scope,确保包含openid以获取新的ID Token if (!scopes.isEmpty()) { body.add(SCOPE_KEY, String.join(" ", scopes)); } return body; }
调用时传入授权客户端的现有scope:
.bodyValue(refreshTokenBody(authorizedClient.getRefreshToken().getTokenValue(), authorizedClient.getAccessToken().getScopes()))
3. 解析并存储刷新后的ID Token
OIDC的refresh_token响应中,新的ID Token会放在响应的额外参数(additionalParameters)里,需要解析并存储:
3.1 解析ID Token
在map处理accessTokenResponse的逻辑中,取出id_token:
.map(accessTokenResponse -> { OAuth2RefreshToken refreshToken = Optional.ofNullable(accessTokenResponse.getRefreshToken()) .orElse(authorizedClient.getRefreshToken()); // 从额外参数中取出新的ID Token String newIdToken = (String) accessTokenResponse.getAdditionalParameters().get("id_token"); // 可选:验证ID Token的有效性(签名、过期时间等) // 可使用OidcIdTokenDecoder或提供商的SDK进行验证 // 存储ID Token:根据你的存储策略处理,比如存在Session或自定义存储中 storeIdToken(exchange, newIdToken); return new OAuth2AuthorizedClient(authorizedClient.getClientRegistration(), authorizedClient.getPrincipalName(), accessTokenResponse.getAccessToken(), refreshToken); })
3.2 存储ID Token
由于OAuth2AuthorizedClient默认不存储ID Token,需自定义存储逻辑:
- 如果使用Session存储,可直接将ID Token存入
ServerWebExchange的Session:private void storeIdToken(ServerWebExchange exchange, String idToken) { exchange.getSession().doOnNext(session -> session.getAttributes().put("OIDC_ID_TOKEN", idToken)).subscribe(); } - 如果使用自定义
OAuth2AuthorizedClientRepository,可扩展OAuth2AuthorizedClient添加idToken字段,序列化后存储。
4. 更新Token Relay逻辑,转发新的ID Token
在CustomTokenRelayGatewayFilterFactory的过滤逻辑中,取出刷新后的ID Token,添加到转发的请求头中(根据下游服务需求决定头名称,比如X-ID-Token或Authorization: Bearer {id_token}):
@Override public GatewayFilter apply(Config config) { return (exchange, chain) -> { // ... 原有逻辑:获取授权客户端、刷新AccessToken等 // 取出存储的ID Token return exchange.getSession().flatMap(session -> { String idToken = (String) session.getAttributes().get("OIDC_ID_TOKEN"); if (idToken != null) { // 将ID Token添加到请求头 exchange.getRequest().mutate() .header("X-ID-Token", idToken) .build(); } return chain.filter(exchange); }); }; }
5. 异常处理与边界情况
- 如果刷新响应中未返回
id_token,需记录日志并决定是否继续使用旧ID Token(直到其过期)或抛出异常; - 若ID Token验证失败(如签名无效、已过期),需触发重新授权流程;
- 部分提供商刷新时会返回新的
refresh_token,需确保同时更新存储的refresh token(你的现有代码已处理此逻辑)。
内容的提问来源于stack exchange,提问作者Willy De Keyser
相关产品推荐
相关产品推荐

