Spring Security 5中无Spring OAuth的Feign服务间通信配置咨询
解决Spring Security 5资源服务器中Feign的Client Credentials令牌获取与注入问题
在纯Spring Security 5环境下,我们可以借助spring-security-oauth2-client模块来实现client_credentials授权类型的令牌获取,同时自定义Feign拦截器完成令牌注入。以下是具体实现步骤:
1. 配置客户端注册与令牌管理
首先,我们需要配置授权服务器信息和客户端凭证,同时创建负责令牌获取、刷新的核心组件OAuth2AuthorizedClientManager:
@Configuration public class OAuth2ClientConfig { // 从配置文件绑定客户端信息 @Bean public ClientRegistration clientRegistration( @Value("${security.oauth2.client.client-id}") String clientId, @Value("${security.oauth2.client.client-secret}") String clientSecret, @Value("${security.oauth2.client.access-token-uri}") String tokenUri) { return ClientRegistration.withRegistrationId("service-client") .clientId(clientId) .clientSecret(clientSecret) .tokenUri(tokenUri) .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) .build(); } @Bean public RegisteredClientRepository registeredClientRepository(ClientRegistration clientRegistration) { return new InMemoryRegisteredClientRepository(clientRegistration); } // 配置令牌管理器,自动处理令牌的获取、缓存与刷新 @Bean public OAuth2AuthorizedClientManager authorizedClientManager( ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientService authorizedClientService) { OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder .builder() .clientCredentials() .build(); DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager( clientRegistrationRepository, authorizedClientService); authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider); return authorizedClientManager; } }
2. 自定义Feign请求拦截器
替换原Spring OAuth提供的OAuth2FeignRequestInterceptor,实现一个自定义拦截器,通过OAuth2AuthorizedClientManager获取令牌并注入请求头:
@Component public class ClientCredentialsFeignInterceptor implements RequestInterceptor { private static final String AUTHORIZATION_HEADER = "Authorization"; private static final String BEARER_TOKEN_PREFIX = "Bearer "; private final OAuth2AuthorizedClientManager authorizedClientManager; public ClientCredentialsFeignInterceptor(OAuth2AuthorizedClientManager authorizedClientManager) { this.authorizedClientManager = authorizedClientManager; } @Override public void apply(RequestTemplate template) { // 构造client_credentials授权的请求参数 OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("service-client") .principal(new AnonymousAuthenticationToken("feign-client", "feign-client", Collections.emptyList())) .build(); // 获取授权客户端(自动处理令牌获取/过期刷新) OAuth2AuthorizedClient authorizedClient = authorizedClientManager.authorize(authorizeRequest); if (authorizedClient != null && authorizedClient.getAccessToken() != null) { String tokenValue = authorizedClient.getAccessToken().getTokenValue(); template.header(AUTHORIZATION_HEADER, BEARER_TOKEN_PREFIX + tokenValue); } } }
3. 配置Feign使用自定义拦截器
你可以选择两种方式让Feign使用这个拦截器:
全局配置(所有Feign客户端生效)
@Configuration public class FeignGlobalConfig { @Autowired private ClientCredentialsFeignInterceptor clientCredentialsFeignInterceptor; @Bean public RequestInterceptor feignRequestInterceptor() { return clientCredentialsFeignInterceptor; } }
单个客户端配置(仅指定Feign客户端生效)
在Feign客户端接口上绑定专属配置类:
@FeignClient(name = "resource-server-b", configuration = FeignClientConfig.class) public interface ResourceServerBClient { // 定义调用资源服务器B的接口方法 } // 专属配置类 @Configuration public class FeignClientConfig { @Autowired private ClientCredentialsFeignInterceptor clientCredentialsFeignInterceptor; @Bean public RequestInterceptor requestInterceptor() { return clientCredentialsFeignInterceptor; } }
4. 配置文件复用与说明
原有的yaml配置可以直接复用,不需要大幅修改:
security: oauth2: client: client-id: service-id client-secret: secret access-token-uri: http://localhost:8081/oauth/token
关键细节说明
OAuth2AuthorizedClientManager会自动处理令牌的缓存和过期刷新,无需手动维护令牌生命周期;- 拦截器中使用匿名身份作为
principal即可,因为client_credentials授权不需要用户上下文; - 确保项目依赖中引入了
spring-security-oauth2-client和spring-cloud-starter-openfeign(Spring Cloud环境下)。
内容的提问来源于stack exchange,提问作者Denis Stephanov
相关产品推荐
相关产品推荐

