You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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字段;
  • 部分提供商要求刷新请求必须携带openid scope,否则不会返回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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 08:37:40