如何在OpenFeign中复用用户会话的Access Token调用微服务?
统一复用用户Access Token实现Feign跨服务调用
核心前提
要复用用户到后端的Access Token调用微服务,首先得确保该Token具备访问下游微服务的权限:
- 在Keycloak中,给用户认证用的客户端(authorization_code模式的那个)添加下游微服务客户端的角色,或者配置对应的资源权限/scope,让用户的Access Token包含调用微服务的权限声明。
- 下游微服务需配置为Resource Server,校验Token的issuer、audience等参数需与用户Access Token匹配。
实现步骤
1. 实现Feign请求拦截器,从Security上下文提取用户Access Token
弃用之前基于client_credentials的AuthorizedClientServiceOAuth2AuthorizedClientManager,改为直接从当前请求的Security上下文获取用户的Access Token,并自动注入到Feign请求头中:
@Component public class UserAccessTokenFeignInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate template) { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); // 仅在存在用户OAuth2认证上下文时注入Token if (auth instanceof OAuth2AuthenticationToken) { OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken) auth; OAuth2AccessToken accessToken = oauthToken.getPrincipal().getAccessToken(); template.header("Authorization", "Bearer " + accessToken.getTokenValue()); } } }
2. 配置Spring Security自动刷新Token
Spring Security OAuth2框架本身支持用Refresh Token自动刷新过期的Access Token,只需确保你的Security配置正确开启相关支持,刷新后的Token会自动更新到Security上下文,Feign调用就能直接复用新Token:
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth.anyRequest().authenticated()) // 开启OAuth2登录,确保Refresh Token被正确存储 .oauth2Login(oauth2 -> oauth2 .tokenEndpoint(token -> token .accessTokenResponseClient(accessTokenResponseClient()) ) ) // 配置资源服务器,支持JWT Token校验 .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults())); return http.build(); } // 配置支持Refresh Token的Token响应客户端 @Bean public OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient() { DefaultAuthorizationCodeTokenResponseClient client = new DefaultAuthorizationCodeTokenResponseClient(); // 可根据需求自定义ClientRegistration、RestTemplate等配置 return client; } }
3. 处理异步调用场景(可选)
如果你的Feign调用是在异步线程中执行,需要确保Security上下文能传递到异步线程,否则会无法获取用户Token:
- 全局配置线程上下文继承:
@Configuration public class AsyncConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setTaskDecorator(runnable -> { SecurityContext context = SecurityContextHolder.getContext(); return () -> { try { SecurityContextHolder.setContext(context); runnable.run(); } finally { SecurityContextHolder.clearContext(); } }; }); executor.initialize(); return executor; } }
关键注意事项
- 下游微服务的Resource Server配置必须信任用户Access Token的issuer(即你的Keycloak实例),并且Token的
aud(受众)或scope包含微服务的访问权限。 - 用户的Refresh Token有效期需合理配置,确保在用户会话期间能自动刷新Access Token。
- 若遇到Token刷新失败(比如Refresh Token过期),需在业务代码中处理对应的异常,引导用户重新登录。
内容的提问来源于stack exchange,提问作者Stefan Zhelyazkov
相关产品推荐
相关产品推荐

