Inject OAuth2AuthorizedClientManager via constructor.

This commit is contained in:
Olga Maciaszek-Sharma
2022-09-30 14:30:30 +02:00
parent afbe0b232b
commit 77072d96e4
11 changed files with 89 additions and 83 deletions

View File

@@ -75,6 +75,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Sort;
import org.springframework.security.oauth2.client.AuthorizedClientServiceOAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.OAuth2ClientContext;
@@ -352,7 +353,7 @@ public class FeignAutoConfiguration {
@ConditionalOnClass(OAuth2ClientContext.class)
@ConditionalOnProperty("feign.oauth2.enabled")
@Deprecated // spring-security-oauth2 reached EOL
protected static class Oauth2FeignConfiguration {
protected static class DeprecatedOauth2FeignConfiguration {
@ConditionalOnBean({ RetryLoadBalancerInterceptor.class, OAuth2ClientContext.class,
OAuth2ProtectedResourceDetails.class })
@@ -374,20 +375,36 @@ public class FeignAutoConfiguration {
@Bean
@ConditionalOnMissingBean(OAuth2FeignRequestInterceptor.class)
@ConditionalOnBean({OAuth2ClientContext.class, OAuth2ProtectedResourceDetails.class})
@ConditionalOnBean({ OAuth2ClientContext.class, OAuth2ProtectedResourceDetails.class })
public RequestInterceptor oauth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext,
OAuth2ProtectedResourceDetails resource, List<OAuth2FeignRequestInterceptorConfigurer> configurers) {
OAuth2ProtectedResourceDetails resource, List<OAuth2FeignRequestInterceptorConfigurer> configurers) {
return buildWithConfigurers(oAuth2ClientContext, resource, configurers);
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(OAuth2AuthorizedClientManager.class)
@ConditionalOnProperty("feign.oauth2.enabled")
protected static class Oauth2FeignConfiguration {
@Bean
@ConditionalOnBean({ OAuth2AuthorizedClientService.class, ClientRegistrationRepository.class })
@ConditionalOnMissingBean
OAuth2AuthorizedClientManager feignOAuth2AuthorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientService oAuth2AuthorizedClientService) {
return new AuthorizedClientServiceOAuth2AuthorizedClientManager(clientRegistrationRepository,
oAuth2AuthorizedClientService);
}
@Bean
@ConditionalOnBean(OAuth2AuthorizedClientManager.class)
public OAuth2AccessTokenInterceptor defaultOAuth2AccessTokenInterceptor(
@Value("${spring.cloud.openfeign.oauth2.clientRegistrationId:}") String clientRegistrationId,
OAuth2AuthorizedClientService oAuth2AuthorizedClientService,
ClientRegistrationRepository clientRegistrationRepository) {
return new OAuth2AccessTokenInterceptor(clientRegistrationId, oAuth2AuthorizedClientService,
clientRegistrationRepository);
@Value("${feign.oauth2.clientRegistrationId:}") String clientRegistrationId,
OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager) {
return new OAuth2AccessTokenInterceptor(clientRegistrationId, oAuth2AuthorizedClientManager);
}
}

View File

@@ -27,26 +27,23 @@ import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.client.AuthorizedClientServiceOAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* A {@link RequestInterceptor} for OAuth2 Feign Requests. By default, it uses the
* {@link AuthorizedClientServiceOAuth2AuthorizedClientManager } to get
* {@link OAuth2AuthorizedClient } that holds an {@link OAuth2AccessToken }. If the user
* has specified an OAuth2 {@code clientId} using the
* {@code spring.cloud.openfeign.oauth2.clientId} property, it will be used to retrieve
* the token. If the token is not retrieved or the {@code clientId} has not been
* specified, the {@code serviceId} retrieved from the {@code url} host segment will be
* used. This approach is convenient for load-balanced Feign clients. For
* non-load-balanced ones, the property-based {@code clientId} is a suitable approach.
* {@link OAuth2AuthorizedClientManager } to get {@link OAuth2AuthorizedClient } that
* holds an {@link OAuth2AccessToken }. If the user has specified an OAuth2
* {@code clientRegistrationId} using the {@code feign.oauth2.clientRegistrationId}
* property, it will be used to retrieve the token. If the token is not retrieved or the
* {@code clientRegistrationId} has not been specified, the {@code serviceId} retrieved
* from the {@code url} host segment will be used. This approach is convenient for
* load-balanced Feign clients. For non-load-balanced ones, the property-based
* {@code clientRegistrationId} is a suitable approach.
*
* @author Dangzhicairang(小水牛)
* @author Olga Maciaszek-Sharma
@@ -70,34 +67,26 @@ public class OAuth2AccessTokenInterceptor implements RequestInterceptor {
private final String clientRegistrationId;
private OAuth2AuthorizedClientManager authorizedClientManager;
public void setAuthorizedClientManager(OAuth2AuthorizedClientManager authorizedClientManager) {
this.authorizedClientManager = authorizedClientManager;
}
private final OAuth2AuthorizedClientManager authorizedClientManager;
private static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken("anonymous",
"anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
public OAuth2AccessTokenInterceptor(OAuth2AuthorizedClientService oAuth2AuthorizedClientService,
ClientRegistrationRepository clientRegistrationRepository) {
this(null, oAuth2AuthorizedClientService, clientRegistrationRepository);
public OAuth2AccessTokenInterceptor(OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager) {
this(null, oAuth2AuthorizedClientManager);
}
public OAuth2AccessTokenInterceptor(String clientRegistrationId,
OAuth2AuthorizedClientService oAuth2AuthorizedClientService,
ClientRegistrationRepository clientRegistrationRepository) {
this(BEARER, AUTHORIZATION, clientRegistrationId, oAuth2AuthorizedClientService, clientRegistrationRepository);
OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager) {
this(BEARER, AUTHORIZATION, clientRegistrationId, oAuth2AuthorizedClientManager);
}
public OAuth2AccessTokenInterceptor(String tokenType, String header, String clientRegistrationId,
OAuth2AuthorizedClientService oAuth2AuthorizedClientService,
ClientRegistrationRepository clientRegistrationRepository) {
OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager) {
this.tokenType = tokenType;
this.header = header;
this.clientRegistrationId = clientRegistrationId;
this.authorizedClientManager = new AuthorizedClientServiceOAuth2AuthorizedClientManager(
clientRegistrationRepository, oAuth2AuthorizedClientService);
this.authorizedClientManager = oAuth2AuthorizedClientManager;
}
@Override
@@ -144,9 +133,9 @@ public class OAuth2AccessTokenInterceptor implements RequestInterceptor {
private static String getServiceId(RequestTemplate template) {
Target<?> feignTarget = template.feignTarget();
Assert.notNull(feignTarget, "feignTarget may not be null");
Assert.notNull(feignTarget, "FeignTarget may not be null.");
String url = feignTarget.url();
Assert.hasLength(url, "url may not be empty");
Assert.hasLength(url, "Url may not be empty.");
final URI originalUri = URI.create(url);
return originalUri.getHost();
}

View File

@@ -42,6 +42,7 @@ import org.springframework.security.oauth2.common.OAuth2AccessToken;
* @author Joao Pedro Evangelista
* @author Tim Ysewyn
* @since 3.0.0
* @deprecated in favour of {@link OAuth2AccessTokenInterceptor}
*/
@Deprecated // spring-security-oauth2 reached EOL
public class OAuth2FeignRequestInterceptor implements RequestInterceptor {

View File

@@ -39,7 +39,9 @@ import org.springframework.security.oauth2.client.token.grant.password.ResourceO
*
* @author Wojciech Mąka
* @since 3.1.1
* @deprecated since spring-security-oauth2 reached EOL
*/
@Deprecated // spring-security-oauth2 reached EOL
public class OAuth2FeignRequestInterceptorBuilder {
private AccessTokenProvider accessTokenProvider;

View File

@@ -26,7 +26,9 @@ import org.springframework.security.oauth2.client.token.AccessTokenProvider;
*
* @author Wojciech Mąka
* @since 3.1.1
* @deprecated since spring-security-oauth2 reached EOL
*/
@Deprecated // spring-security-oauth2 reached EOL
@FunctionalInterface
public interface OAuth2FeignRequestInterceptorConfigurer {

View File

@@ -23,7 +23,7 @@
{
"name": "feign.circuitbreaker.group.enabled",
"type": "java.lang.Boolean",
"description": "If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker with with group.",
"description": "If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker with group.",
"defaultValue": "false"
},
{
@@ -81,9 +81,9 @@
"defaultValue": "false"
},
{
"name": "feign.oauth2.registrationClientId",
"name": "feign.oauth2.clientRegistrationId",
"type": "java.lang.String",
"description": "Provides a clientId to be used with OAuth2.",
"description": "Provides a clientRegistrationId to be used with OAuth2.",
"defaultValue": ""
}
]

View File

@@ -127,19 +127,17 @@ class FeignAutoConfigurationTests {
@Test
void shouldInstantiateFeignOAuth2FeignRequestInterceptorWithCustomAccessTokenProviderInterceptor() {
runner.withPropertyValues("feign.oauth2.enabled=true")
.withBean(MockOAuth2ClientContext.class, "token")
.withBean(BaseOAuth2ProtectedResourceDetails.class)
.withBean(CustomOAuth2FeignRequestInterceptorConfigurer.class).run(ctx -> {
assertOauth2FeignRequestInterceptorExists(ctx);
assertAccessTokenProviderInterceptorExists(ctx, BasicAuthenticationInterceptor.class);
});
runner.withPropertyValues("feign.oauth2.enabled=true").withBean(MockOAuth2ClientContext.class, "token")
.withBean(BaseOAuth2ProtectedResourceDetails.class)
.withBean(CustomOAuth2FeignRequestInterceptorConfigurer.class).run(ctx -> {
assertOauth2FeignRequestInterceptorExists(ctx);
assertAccessTokenProviderInterceptorExists(ctx, BasicAuthenticationInterceptor.class);
});
}
@Test
void shouldInstantiateFeignOAuth2FeignRequestInterceptor() {
runner.withPropertyValues("spring.cloud.openfeign.oauth2.enabled=true",
"spring.cloud.openfeign.oauth2.clientRegistrationId=feign-client")
runner.withPropertyValues("feign.oauth2.enabled=true", "feign.oauth2.clientRegistrationId=feign-client")
.withBean(OAuth2AuthorizedClientService.class, () -> mock(OAuth2AuthorizedClientService.class))
.withBean(ClientRegistrationRepository.class, () -> mock(ClientRegistrationRepository.class))
.run(ctx -> {
@@ -154,21 +152,18 @@ class FeignAutoConfigurationTests {
}
private void assertAccessTokenProviderInterceptorExists(ConfigurableApplicationContext ctx,
Class<? extends ClientHttpRequestInterceptor> clazz) {
Class<? extends ClientHttpRequestInterceptor> clazz) {
AssertableApplicationContext context = AssertableApplicationContext.get(() -> ctx);
assertThat(context).getBean(OAuth2FeignRequestInterceptor.class)
.extracting("accessTokenProvider")
assertThat(context).getBean(OAuth2FeignRequestInterceptor.class).extracting("accessTokenProvider")
.extracting("interceptors").asList().first().isInstanceOf(clazz);
}
private void assertAccessTokenProviderInterceptorNotExists(ConfigurableApplicationContext ctx,
Class<? extends ClientHttpRequestInterceptor> clazz) {
Class<? extends ClientHttpRequestInterceptor> clazz) {
AssertableApplicationContext context = AssertableApplicationContext.get(() -> ctx);
assertThat(context).getBean(OAuth2FeignRequestInterceptor.class)
.extracting("accessTokenProvider")
.extracting("interceptors").asList()
.filteredOn(obj -> clazz.isAssignableFrom(obj.getClass()))
.isEmpty();
assertThat(context).getBean(OAuth2FeignRequestInterceptor.class).extracting("accessTokenProvider")
.extracting("interceptors").asList().filteredOn(obj -> clazz.isAssignableFrom(obj.getClass()))
.isEmpty();
}
private void assertOauth2AccessTokenInterceptorExists(ConfigurableApplicationContext ctx) {

View File

@@ -27,9 +27,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
@@ -49,12 +47,6 @@ import static org.mockito.Mockito.when;
*/
class OAuth2AccessTokenInterceptorTests {
private final ClientRegistrationRepository mockClientRegistrationRepository = mock(
ClientRegistrationRepository.class);
private final OAuth2AuthorizedClientService mockOAuth2AuthorizedClientService = mock(
OAuth2AuthorizedClientService.class);
private final OAuth2AuthorizedClientManager mockOAuth2AuthorizedClientManager = mock(
OAuth2AuthorizedClientManager.class);
@@ -74,10 +66,8 @@ class OAuth2AccessTokenInterceptorTests {
@Test
void shouldThrowExceptionWhenNoTokenAcquired() {
oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2AuthorizedClientService,
mockClientRegistrationRepository);
oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2AuthorizedClientManager);
when(mockOAuth2AuthorizedClientManager.authorize(any())).thenReturn(null);
oAuth2AccessTokenInterceptor.setAuthorizedClientManager(mockOAuth2AuthorizedClientManager);
assertThatExceptionOfType(IllegalStateException.class)
.isThrownBy(() -> oAuth2AccessTokenInterceptor.apply(requestTemplate))
@@ -86,10 +76,10 @@ class OAuth2AccessTokenInterceptorTests {
@Test
void shouldAcquireValidToken() {
oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2AuthorizedClientService,
mockClientRegistrationRepository);
when(mockOAuth2AuthorizedClientManager.authorize(any())).thenReturn(validTokenOAuth2AuthorizedClient());
oAuth2AccessTokenInterceptor.setAuthorizedClientManager(mockOAuth2AuthorizedClientManager);
oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2AuthorizedClientManager);
when(mockOAuth2AuthorizedClientManager.authorize(
argThat((OAuth2AuthorizeRequest request) -> ("test").equals(request.getClientRegistrationId()))))
.thenReturn(validTokenOAuth2AuthorizedClient());
oAuth2AccessTokenInterceptor.apply(requestTemplate);
@@ -98,12 +88,10 @@ class OAuth2AccessTokenInterceptorTests {
@Test
void shouldAcquireValidTokenFromServiceId() {
oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2AuthorizedClientService,
mockClientRegistrationRepository);
when(mockOAuth2AuthorizedClientManager.authorize(
argThat((OAuth2AuthorizeRequest request) -> ("test").equals(request.getClientRegistrationId()))))
.thenReturn(validTokenOAuth2AuthorizedClient());
oAuth2AccessTokenInterceptor.setAuthorizedClientManager(mockOAuth2AuthorizedClientManager);
oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2AuthorizedClientManager);
oAuth2AccessTokenInterceptor.apply(requestTemplate);
@@ -111,13 +99,12 @@ class OAuth2AccessTokenInterceptorTests {
}
@Test
void shouldAcquireValidTokenFromSpecifiedClientId() {
void shouldAcquireValidTokenFromSpecifiedClientRegistrationId() {
oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(DEFAULT_CLIENT_REGISTRATION_ID,
mockOAuth2AuthorizedClientService, mockClientRegistrationRepository);
mockOAuth2AuthorizedClientManager);
when(mockOAuth2AuthorizedClientManager
.authorize(argThat((OAuth2AuthorizeRequest request) -> (DEFAULT_CLIENT_REGISTRATION_ID)
.equals(request.getClientRegistrationId())))).thenReturn(validTokenOAuth2AuthorizedClient());
oAuth2AccessTokenInterceptor.setAuthorizedClientManager(mockOAuth2AuthorizedClientManager);
oAuth2AccessTokenInterceptor.apply(requestTemplate);