我有一个Spring Web应用程序,该应用程序具有为其API端点配置的oauth2资源服务器,以及用于其进行的REST调用的完全不同的oauth2客户端。 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授予类型,它将起作用,并且在需要时会自动获取一个新令牌。但是由于某些原因,我不能对密码授予类型进行相同的操作。
编辑: 因此,由于以下原因,我设法解决了这个问题:1。 当我还为refreshToken配置WebClient时,刷新令牌过期时会崩溃。但是当您在此之后再次执行请求时,它将获得一个新令牌。因此,我不得不将API调用包装在try catch中,如果错误是我所关心的错误,我将再次调用该API。这不是一个非常优雅的解决方案,但它可以工作。
答案 0 :(得分:0)
here中明确提到,Spring会在过期时自动刷新访问令牌。
我认为您需要使用过的“反应性”等效类。请尝试使用此代码段。
@Configuration
@RequiredArgsConstructor
public class MyClientConfig {
@Bean
WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client = new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oauth2Client.setDefaultClientRegistrationId("my-client-authorization");
return WebClient.builder()
.filter(oauth2Client)
.baseUrl("https://the.api.host.to.call")
.build();
}
@Bean
public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
ReactiveClientRegistrationRepository clientRegistrationRepository,
ReactiveOAuth2AuthorizedClientService authorizedClientService
) {
ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
.password()
.build();
AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager result = new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
clientRegistrationRepository,
authorizedClientService
);
result.setAuthorizedClientProvider(authorizedClientProvider);
result.setContextAttributesMapper(oAuth2AuthorizeRequest -> Mono.just(Map.of(
OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, "user",
OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, "password"
)));
return result;
}
}