Spring Security Reactive OAuth2 Client:自定义刷新端点方案咨询
解决Spring OAuth2客户端适配分离式刷新端点的问题
问题背景
我在通过OAuth2授权码流访问受保护资源时遇到以下问题:对接的授权服务器会在初始访问令牌请求中返回刷新令牌,但该服务器的刷新端点与令牌端点相互独立。当前Spring客户端配置如下:
spring.security.oauth2.client.provider.test.authorization-uri=http://localhost:8085/oauth/authorize spring.security.oauth2.client.provider.test.token-uri=http://localhost:8085/oauth/token
根据OAuth2 RFC标准,这种分离端点的设计并不合规,因此Spring Security未提供refresh-uri配置项。但我无法修改授权服务器的实现,只能在客户端侧调整刷新令牌请求的目标URI。
已尝试的方案
- 查阅Spring Security文档,发现自定义刷新令牌请求仅支持修改请求参数和请求头,无法直接替换请求URI;
- 尝试自定义
WebClient,但最终请求仍会使用客户端提供者配置中的token-uri(参考AbstractWebClientReactiveOAuth2AccessTokenResponseClient.java源码); - 考虑过在请求流程中修改
OAuth2RefreshTokenGrantRequest来覆盖配置的令牌URI,但觉得这种方式不够简便,希望找到更优解。
依赖版本
spring-boot-starter-webflux:3.2.4spring-boot-starter-security:3.2.4spring-security-oauth2-client:6.2.3
可行解决方案
方案一:自定义刷新令牌响应客户端
通过继承WebClientReactiveOAuth2RefreshTokenTokenResponseClient并重写getTokenResponse方法,直接替换刷新请求的目标URI:
import org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest; import org.springframework.security.oauth2.client.endpoint.WebClientReactiveOAuth2RefreshTokenTokenResponseClient; import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; import reactor.core.publisher.Mono; public class CustomRefreshTokenResponseClient extends WebClientReactiveOAuth2RefreshTokenTokenResponseClient { // 替换为授权服务器实际的刷新端点 private static final String CUSTOM_REFRESH_URI = "http://localhost:8085/oauth/refresh"; @Override public Mono<OAuth2AccessTokenResponse> getTokenResponse(OAuth2RefreshTokenGrantRequest grantRequest) { return getWebClient() .post() .uri(CUSTOM_REFRESH_URI) .headers(headers -> headers.setAll(grantRequest.getClientRegistration().getClientAuthenticationMethod().getHeaders( grantRequest.getClientRegistration().getClientId(), grantRequest.getClientRegistration().getClientSecret()))) .bodyValue(createRequestBody(grantRequest)) .retrieve() .bodyToMono(OAuth2AccessTokenResponse.class); } }
然后在配置类中注册这个自定义客户端:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient; import org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest; @Configuration public class OAuth2ClientConfig { @Bean public ReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> customRefreshTokenResponseClient() { return new CustomRefreshTokenResponseClient(); } }
方案二:通过WebClient过滤器替换URI
更简洁的方式是在WebClient的过滤器中拦截刷新令牌请求,替换目标URI:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.client.endpoint.WebClientReactiveOAuth2RefreshTokenTokenResponseClient; import org.springframework.web.reactive.function.client.ExchangeFilterFunction; import reactor.core.publisher.Mono; @Configuration public class OAuth2ClientConfig { private static final String CUSTOM_REFRESH_URI = "http://localhost:8085/oauth/refresh"; @Bean public WebClientReactiveOAuth2RefreshTokenTokenResponseClient refreshTokenResponseClient() { WebClientReactiveOAuth2RefreshTokenTokenResponseClient client = new WebClientReactiveOAuth2RefreshTokenTokenResponseClient(); client.setWebClient(client.getWebClient().mutate() .filter(ExchangeFilterFunction.ofRequestProcessor(request -> { // 仅拦截原令牌端点的请求,替换为刷新端点 if (request.url().getPath().equals("/oauth/token")) { return Mono.just(request.mutate().uri(CUSTOM_REFRESH_URI).build()); } return Mono.just(request); })) .build()); return client; } }
内容的提问来源于stack exchange,提问作者Brian McDonnell
相关产品推荐
相关产品推荐

