PAYATHON 2026

如何让 OpenFeign 复用 Keycloak 用户 Access Token 并自动刷新?

数据阿明

可以,前提是下游微服务允许使用该用户的 Access Token。让 OpenFeign 使用用户登录对应的 OAuth2AuthorizedClientManager,就可以复用未过期的令牌;需要刷新时,管理器会在存在有效 Refresh Token 的情况下刷新令牌。

这里统一的是用户授权的获取、存储和使用流程。刷新仍会产生新的 Access Token;ID Token、Access Token 和 Refresh Token 各有用途,不能合并成一个令牌。

为什么目前会产生两组令牌

authorization_code 获得的是用户授权,client_credentials 获得的是应用自身的授权。两组令牌通常代表不同身份,是否合并取决于下游接口需要按用户权限还是应用权限执行操作。

AuthorizedClientServiceOAuth2AuthorizedClientManager 并非只能处理 client_credentials。它面向不依赖当前 Servlet 请求的场景,支持哪些授权方式由 OAuth2AuthorizedClientProvider 决定。其默认 provider 使用 client_credentials,但可以替换。Spring Security 实现源码

在用户请求中配置授权客户端管理器

以下示例针对通过 oauth2Login() 登录的 Servlet 应用。假设登录使用的 registration ID 是 keycloak,并且登录结果已经保存为 Spring Security 的 OAuth2AuthorizedClient。

为当前请求配置 DefaultOAuth2AuthorizedClientManager,启用 authorization_code 和 refresh_token:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;

@Configuration
public class UserOAuth2ClientConfig {

    @Bean
    public OAuth2AuthorizedClientManager authorizedClientManager(
            ClientRegistrationRepository registrations,
            OAuth2AuthorizedClientRepository authorizedClients) {

        var manager = new DefaultOAuth2AuthorizedClientManager(
                registrations, authorizedClients);

        manager.setAuthorizedClientProvider(
                OAuth2AuthorizedClientProviderBuilder.builder()
                        .authorizationCode()
                        .refreshToken()
                        .build());

        return manager;
    }
}

这里应使用登录流程所使用的 OAuth2AuthorizedClientRepository。如果登录时自行向 Keycloak 请求令牌,且只将令牌保存在自定义字段中,管理器不会自动发现它们,需要先接入授权客户端的加载与保存流程。

DefaultOAuth2AuthorizedClientManager 会通过 repository 保存成功授权或刷新后的授权客户端。Spring Security 授权客户端管理说明

让 OpenFeign 选择同一个登录配置

保留已有的 spring.security.oauth2.client.registration.keycloak 登录配置,让 OpenFeign 使用相同的 registration ID:

spring:
  cloud:
    openfeign:
      oauth2:
        enabled: true
        clientRegistrationId: keycloak

这里的 keycloak 是 Spring 配置中 registration 的名称,不一定等于 Keycloak 中的 client-id。项目还需要包含 spring-boot-starter-oauth2-client。

OpenFeign 的 OAuth2 支持会调用 OAuth2AuthorizedClientManager 获取令牌,然后添加 Authorization: Bearer ... 请求头。Spring Cloud OpenFeign OAuth2 配置说明

这一方案要求拦截器将当前用户的 Authentication 传给管理器。当前官方源码中的 OAuth2AccessTokenInterceptor 会从 SecurityContextHolder 获取该身份;由于题目没有提供依赖版本,请核对项目所用版本是否具有相同行为。若自行实现拦截器,也应使用当前用户身份调用 authorize(...),再读取返回的 Access Token。OpenFeign 拦截器实现

自动刷新有哪些边界

自动刷新通常在调用管理器时按需触发,不是后台定时任务。它要求已保存的授权客户端包含 Refresh Token,且该令牌仍可使用。仅读取并转发存储中的 Access Token,不会触发刷新。

还要区分两类失败:

  • Refresh Token 已失效:应用需要处理授权失败,必要时引导用户重新登录。
  • 下游返回 401:不能直接认定 Access Token 过期,也可能是 audience、签名或其他校验失败;不能假定 Feign 会自动刷新并重试业务请求。

管理器在收到 invalid_grant 等特定授权失败时,默认可以移除已保存的授权客户端,应用仍需处理后续登录流程。DefaultOAuth2AuthorizedClientManager API

下游必须接受用户令牌

复用前,应确认 Access Token 的 aud 包含目标服务所要求的 audience,并且用户具有所需的 scope 或角色。Keycloak 可以通过 Audience mapper 或相关角色配置添加 audience;下游服务应执行对应校验。不要通过关闭 audience 校验来实现复用。Keycloak audience 配置说明

这条代表用户调用的链路可以不再使用独立的 client_credentials 配置,但不能因此删除所有微服务在 Keycloak 中的资源、角色或 audience 配置。没有用户参与的后台任务,仍应按其身份需求单独设计。

示例依赖当前 Servlet 请求和用户安全上下文。如果 Feign 调用进入 @Async、线程池或消息消费流程,需要另外处理身份传递与授权客户端存储,不能直接假定会话上下文仍然可用。发送给下游的是 Access Token,Refresh Token 应留在负责刷新令牌的后端。

备注:内容仅供参考。