【发布时间】:2021-05-29 20:27:30
【问题描述】:
我尝试使用 Spring WebClient 和 Spring Security OAuth2 Client 在两个应用程序之间进行通信。我需要使用 OAuth2 Client Credentials 授权类型,并对这些应用程序之间的所有请求使用相同的凭据和相同的令牌。在 OAuth2 术语中,资源所有者是应用程序 A 本身,资源服务器是应用程序 B(它是 Keycloak Admin API)。我使用来自 application.yaml 和 servlet 环境的 Spring Boot 自动配置。所以我是这样实现的:
WebClient buildWebClient(ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository, String clientName) {
final ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client = new ServletOAuth2AuthorizedClientExchangeFilterFunction(
clientRegistrationRepository, authorizedClientRepository);
oauth2Client.setDefaultClientRegistrationId(clientName);
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.build();
}
我发现我有一个问题并且无效的令牌(我使用 Keycloak,如果我的 Keycloak 重新启动,那么所有旧令牌都无效)永远不会被删除(你可以在 issue #9477 中看到它)所以我开始调试整个过程ServletOAuth2AuthorizedClientExchangeFilterFunction 中的授权。我发现令牌是应用程序 A 的每个用户保存的。这意味着应用程序 A 的每个用户都有自己的令牌来授权应用程序 B。但这不是我的预期行为我只想在应用程序中使用一个令牌A 用于消耗来自应用程序 B 的资源。
我认为我找到了两个解决方案,但它们都不是最优的。
解决方案 1:
在ServletOAuth2AuthorizedClientExchangeFilterFunction 之后添加另一个默认请求,它将属性中的用户Authentication 替换为应用程序Authentication。问题是它依赖于ServletOAuth2AuthorizedClientExchangeFilterFunction 的内部实现,它使用私有属性AUTHENTICATION_ATTR_NAME = Authentication.class.getName();。
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.defaultRequest(spec -> spec.attributes(attr ->
attr.put(AUTHENTICATION_ATTR_NAME, new CustomApplicaitonAuthentication())
)
.build();
解决方案 2:
使用OAuth2AuthorizedClientRepository 的自定义实现而不是默认的AuthenticatedPrincipalOAuth2AuthorizedClientRepository(由于删除无效令牌的问题我必须这样做),我忽略用户的Authentication 并使用自定义应用程序Authentication。
@Override
public <T extends OAuth2AuthorizedClient> T loadAuthorizedClient(String clientRegistrationId, Authentication principal,
HttpServletRequest request) {
return this.authorizedClientService.loadAuthorizedClient(clientRegistrationId, new CustomApplicaitonAuthentication());
}
@Override
public void saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal,
HttpServletRequest request, HttpServletResponse response) {
this.authorizedClientService.saveAuthorizedClient(authorizedClient, new CustomApplicaitonAuthentication());
}
但我认为我的两种解决方案都不是最优的。还有其他方法可以更直接吗?
【问题讨论】:
标签: spring spring-security oauth-2.0 spring-security-oauth2