Spring Cloud Gateway过期Access Token刷新方案及问题求助
实现Spring Cloud Gateway自动刷新过期Access Token的可行方案
方案一:自定义GatewayFilter实现Token检查与刷新
直接自定义网关过滤器,手动把控Token校验、刷新的全流程,适配Azure AD的特性:
- 步骤1:获取存储的Refresh Token
从网关认证上下文(如SecurityContextRepository)中取出当前用户的Refresh Token——Azure AD授权码流程中,认证成功后会返回该Token,需提前存储(可存在Redis或网关会话中)。 - 步骤2:校验Access Token有效性
解析Access Token的exp字段判断是否过期;或调用Azure AD的令牌验证端点https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/introspect验证Token状态,请求需携带客户端ID和密钥。 - 步骤3:执行Token刷新逻辑
若Token过期,向Azure AD令牌端点https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token发送POST请求,参数包含grant_type=refresh_token、refresh_token=xxx、client_id=xxx、client_secret=xxx(机密客户端需提供)、scope=xxx(与授权时一致)。 - 步骤4:更新Token并转发请求
拿到新的Access Token和Refresh Token后,更新存储的Token信息,再将新Token放入Authorization: Bearer {newToken}请求头,转发给下游服务。
核心代码片段示例:
public class TokenRefreshGatewayFilter implements GatewayFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { return exchange.getPrincipal() .cast(OAuth2AuthenticationToken.class) .flatMap(authToken -> { OAuth2AuthorizedClient authorizedClient = authorizedClientService.loadAuthorizedClient( authToken.getAuthorizedClientRegistrationId(), authToken.getName() ); OAuth2AccessToken accessToken = authorizedClient.getAccessToken(); if (accessToken.isExpired()) { return refreshToken(authorizedClient) .flatMap(newAuthorizedClient -> { authorizedClientService.saveAuthorizedClient(newAuthorizedClient, authToken); exchange.getRequest().mutate() .header("Authorization", "Bearer " + newAuthorizedClient.getAccessToken().getTokenValue()) .build(); return chain.filter(exchange); }); } else { return chain.filter(exchange); } }); } private Mono<OAuth2AuthorizedClient> refreshToken(OAuth2AuthorizedClient authorizedClient) { WebClient webClient = WebClient.create(); return webClient.post() .uri("https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token", "your-tenant-id") .contentType(MediaType.APPLICATION_FORM_URLENCODED) .body(BodyInserters.fromFormData("grant_type", "refresh_token") .with("refresh_token", authorizedClient.getRefreshToken().getTokenValue()) .with("client_id", authorizedClient.getClientRegistration().getClientId()) .with("client_secret", authorizedClient.getClientRegistration().getClientSecret()) .with("scope", String.join(" ", authorizedClient.getClientRegistration().getScopes()))) .retrieve() .bodyToMono(OAuth2AccessTokenResponse.class) .map(tokenResponse -> { OAuth2AccessToken newAccessToken = new OAuth2AccessToken( OAuth2AccessToken.TokenType.BEARER, tokenResponse.getAccessToken().getTokenValue(), tokenResponse.getIssuedAt(), tokenResponse.getExpiresAt() ); OAuth2RefreshToken newRefreshToken = tokenResponse.getRefreshToken() != null ? new OAuth2RefreshToken(tokenResponse.getRefreshToken().getTokenValue(), tokenResponse.getIssuedAt()) : authorizedClient.getRefreshToken(); return new OAuth2AuthorizedClient( authorizedClient.getClientRegistration(), authorizedClient.getPrincipalName(), newAccessToken, newRefreshToken ); }); } @Override public int getOrder() { return -1; // 确保在TokenRelay之前执行 } }
方案二:配置Spring Security OAuth2 Client自动刷新机制
Spring Security OAuth2 Client原生支持Token自动刷新,配合Gateway的TokenRelay即可实现需求:
- 配置要点
- 在网关
application.yml中开启OAuth2 Client自动刷新:spring: security: oauth2: client: registration: azure-ad: client-id: your-client-id client-secret: your-client-secret authorization-grant-type: authorization_code redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}" scope: openid,profile,email,api://your-api-scope/.default provider: azure-ad: issuer-uri: https://login.microsoftonline.com/your-tenant-id/v2.0 authorized-client-service: redis: enabled: true # 集群部署时建议用Redis存储AuthorizedClient - 确保
OAuth2AuthorizedClientService配置正确(集群场景用Redis分布式存储,避免Token信息不一致)。 - 使用
TokenRelayGatewayFilterFactory,此时Spring Security会自动在Access Token过期时调用Refresh Token刷新,TokenRelay负责转发新Token。
- 注:若此前
TokenRelayWithTokenRefreshGatewayFilterFactory出错,大概率是Azure AD端点适配问题,优先尝试此方案。
- 在网关
方案三:采用Azure AD的On-Behalf-Of(OBO)流程
如果下游服务也需调用Azure AD保护的API,OBO流程更适配:
- 流程说明
- 网关收到用户的Access Token后,向Azure AD令牌端点发送OBO请求,参数包含
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer、assertion=用户的Access Token、scope=下游API的scope、client_id、client_secret。 - Azure AD返回针对下游服务的专属Access Token,网关将该Token转发给下游。
- 当OBO Token过期时,网关可再次发起OBO请求,只要用户的Refresh Token有效,就能获取新的OBO Token。
- 网关收到用户的Access Token后,向Azure AD令牌端点发送OBO请求,参数包含
- 优势:无需存储用户的Refresh Token,直接用用户Token换取下游服务的Token,安全性更高,且Azure AD自动管理Token生命周期。
通用注意事项
- 无论采用哪种方案,需确保Refresh Token的有效性:Azure AD的Refresh Token存在过期时间,且多次刷新后旧Token会失效,需及时更新存储的Token。
- 网关集群部署时,必须使用分布式
OAuth2AuthorizedClientService(如Redis实现),避免节点间Token信息不一致。
内容的提问来源于stack exchange,提问作者dsreddy
相关产品推荐
相关产品推荐

