【发布时间】:2020-10-31 08:21:36
【问题描述】:
我有一个 Spring Web 应用程序,它有一个为其 API 端点配置的 oauth2 资源服务器和一个完全不同的 oauth2 客户端,用于它进行的 REST 调用。 oauth2 客户端需要是密码授权类型。用户名和密码是固定的(不是来自 HTTP 请求)。我的问题是 30 分钟后访问令牌和刷新令牌都过期了,所以没有办法进行刷新。我希望 Spring Security 只会要求新的访问令牌,但不会。它使用过期的端点调用 REST 端点并返回 403。 这是我所拥有的:
application.yml:
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://our.idp.keycloak.host/auth/realms/firstrealm
client:
registration:
my-client-authorization:
client-id: my_client
client-secret: ${CLIENT_SECRET}
authorization-grant-type: password
scope: openid, profile
provider:
my-client-authorization:
token-uri: https://our.idp.keycloak.host/auth/realms/secondrealm/protocol/openid-connect/token
MyClientConfig.java:
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.security.oauth2.client.OAuth2AuthorizationContext;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
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;
import org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;
import java.util.Map;
@Configuration
@RequiredArgsConstructor
public class MyClientConfig {
@Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client = new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oauth2Client.setDefaultClientRegistrationId("my-client-authorization");
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.baseUrl("https://the.api.host.to.call")
.build();
}
@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository
) {
OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
.password()
.build();
DefaultOAuth2AuthorizedClientManager result = new DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository,
authorizedClientRepository
);
result.setAuthorizedClientProvider(authorizedClientProvider);
result.setContextAttributesMapper(oAuth2AuthorizeRequest -> Map.of(
OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, "user",
OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, "password"
));
return result;
}
}
API 调用本身:
private <T> T callApi(Function<UriBuilder, URI> uriFunction, Class<T> resultType) {
return this.webClient
.get()
.uri(uriFunction)
.retrieve()
.bodyToMono(resultType)
.block();
}
我第一次调用它时它就起作用了。但是 30 分钟后,令牌已经死了,我不知道如何获得一个新的。 如果我将其切换为 client_credentials 授权类型,它会起作用,它会在需要时自动获取新令牌。但由于某种原因,我不能对密码授予类型做同样的事情。
编辑: 所以我设法解决了这个问题:https://github.com/spring-projects/spring-security/issues/8831。 当我也为 refreshToken 配置 WebClient 时,它会在刷新令牌过期时崩溃。但是当你之后再次发出请求时,它会获得一个新的令牌。所以我不得不将 API 调用包装在 try catch 中,如果错误是我关心的错误,我会再次调用 API。这不是一个非常优雅的解决方案,但它确实有效。
【问题讨论】:
-
我的回答能解决你的问题吗?
标签: spring spring-security spring-webflux