Browse Source

Refactor. Add docs. Fix dependencies.

pull/767/head
Olga Maciaszek-Sharma 2 years ago
parent
commit
60fc4a53e7
  1. 13
      docs/src/main/asciidoc/spring-cloud-openfeign.adoc
  2. 1
      spring-cloud-openfeign-core/pom.xml
  3. 9
      spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java
  4. 116
      spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptor.java
  5. 6
      spring-cloud-openfeign-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json
  6. 15
      spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignAutoConfigurationTests.java
  7. 136
      spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptorTests.java
  8. 2
      spring-cloud-openfeign-dependencies/pom.xml

13
docs/src/main/asciidoc/spring-cloud-openfeign.adoc

@ -811,6 +811,19 @@ When the flag is set to true, and the oauth2 client context resource details are @@ -811,6 +811,19 @@ When the flag is set to true, and the oauth2 client context resource details are
Sometimes, when load balancing is enabled for Feign clients, you may want to use load balancing for fetching access tokens, too. To do so, you should ensure that the load balancer is on the classpath (spring-cloud-starter-loadbalancer) and explicitly enable load balancing for OAuth2FeignRequestInterceptor by setting the following flag:
----
feign.oauth2.load-balanced=true
When the flag is set to true, and the oauth2 client context resource details are present, a bean of class `OAuth2AccessTokenInterceptor` is created. Before each request, the interceptor resolves the required access token and includes it as a header.
`OAuth2AccessTokenInterceptor` uses the `AuthorizedClientServiceOAuth2AuthorizedClientManager` to get `OAuth2AuthorizedClient` that holds an `OAuth2AccessToken`. If the user has specified an OAuth2 `clientId` using the `spring.cloud.openfeign.oauth2.clientId` property, it will be used to retrieve the token. If the token is not retrieved or the `clientId` has not been specified, the `serviceId` retrieved from the `url` host segment will be used.
TIP:: Using the `serviceId` as OAuth2 client id is convenient for load-balanced Feign clients. For non-load-balanced ones, the property-based `clientId` is a suitable approach.
=== Transform the load-balanced HTTP request
You can use the selected `ServiceInstance` to transform the load-balanced HTTP Request.
For `Request`, you need to implement and define `LoadBalancerFeignRequestTransformer`, as follows:
[source,java,indent=0]
----
== Configuration properties

1
spring-cloud-openfeign-core/pom.xml

@ -228,6 +228,7 @@ @@ -228,6 +228,7 @@
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<profiles>

9
spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java

@ -51,7 +51,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -51,7 +51,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.interceptor.CacheInterceptor;
import org.springframework.cloud.client.actuator.HasFeatures;
@ -383,13 +382,11 @@ public class FeignAutoConfiguration { @@ -383,13 +382,11 @@ public class FeignAutoConfiguration {
@Bean
@ConditionalOnBean({OAuth2AuthorizedClientService.class, ClientRegistrationRepository.class})
public OAuth2AccessTokenInterceptor defaultOAuth2AccessTokenInterceptor(
@Value("${spring.cloud.openfeign.oauth2.specifiedClientIds:}") List<String> specifiedClientIds,
OAuth2ClientProperties oAuth2ClientProperties,
@Value("${spring.cloud.openfeign.oauth2.clientId:}") String clientId,
OAuth2AuthorizedClientService oAuth2AuthorizedClientService,
ClientRegistrationRepository clientRegistrationRepository) {
return new OAuth2AccessTokenInterceptor(specifiedClientIds, oAuth2ClientProperties,
oAuth2AuthorizedClientService, clientRegistrationRepository);
return new OAuth2AccessTokenInterceptor(clientId, oAuth2AuthorizedClientService,
clientRegistrationRepository);
}
}

116
spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptor.java

@ -16,17 +16,14 @@ @@ -16,17 +16,14 @@
package org.springframework.cloud.openfeign.security;
import java.net.URI;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import feign.Target;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
@ -38,16 +35,22 @@ import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; @@ -38,16 +35,22 @@ 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;
/**
* RequestInterceptor for OAuth2 Feign Requests. By default, It uses the
* A {@link RequestInterceptor} for OAuth2 Feign Requests. By default, it uses the
* {@link AuthorizedClientServiceOAuth2AuthorizedClientManager } to get
* {@link OAuth2AuthorizedClient } that hold an {@link OAuth2AccessToken }. Use the
* Client(s) from properties if not specific the field
* {@link OAuth2AccessTokenInterceptor#specifiedClientIds}
* {@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.
*
* @author Dangzhicairang(小水牛)
* @author Olga Maciaszek-Sharma
* @since 4.0.0
*/
public class OAuth2AccessTokenInterceptor implements RequestInterceptor {
@ -66,9 +69,7 @@ public class OAuth2AccessTokenInterceptor implements RequestInterceptor { @@ -66,9 +69,7 @@ public class OAuth2AccessTokenInterceptor implements RequestInterceptor {
private final String header;
private final List<String> specifiedClientIds;
private final OAuth2ClientProperties oAuth2ClientProperties;
private final String clientId;
private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService;
@ -79,69 +80,56 @@ public class OAuth2AccessTokenInterceptor implements RequestInterceptor { @@ -79,69 +80,56 @@ public class OAuth2AccessTokenInterceptor implements RequestInterceptor {
}
private static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken("anonymous",
"anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
"anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
public OAuth2AccessTokenInterceptor(OAuth2ClientProperties oAuth2ClientProperties,
OAuth2AuthorizedClientService oAuth2AuthorizedClientService,
ClientRegistrationRepository clientRegistrationRepository) {
this(new ArrayList<>(), oAuth2ClientProperties, oAuth2AuthorizedClientService, clientRegistrationRepository);
public OAuth2AccessTokenInterceptor(OAuth2AuthorizedClientService oAuth2AuthorizedClientService,
ClientRegistrationRepository clientRegistrationRepository) {
this(null, oAuth2AuthorizedClientService, clientRegistrationRepository);
}
public OAuth2AccessTokenInterceptor(List<String> specifiedClientIds, OAuth2ClientProperties oAuth2ClientProperties,
OAuth2AuthorizedClientService oAuth2AuthorizedClientService,
ClientRegistrationRepository clientRegistrationRepository) {
this(BEARER, AUTHORIZATION, specifiedClientIds, oAuth2ClientProperties, oAuth2AuthorizedClientService,
clientRegistrationRepository);
public OAuth2AccessTokenInterceptor(String clientId, OAuth2AuthorizedClientService oAuth2AuthorizedClientService,
ClientRegistrationRepository clientRegistrationRepository) {
this(BEARER, AUTHORIZATION, clientId, oAuth2AuthorizedClientService, clientRegistrationRepository);
}
public OAuth2AccessTokenInterceptor(String tokenType, String header, List<String> specifiedClientIds,
OAuth2ClientProperties oAuth2ClientProperties, OAuth2AuthorizedClientService oAuth2AuthorizedClientService,
ClientRegistrationRepository clientRegistrationRepository) {
public OAuth2AccessTokenInterceptor(String tokenType, String header, String clientId,
OAuth2AuthorizedClientService oAuth2AuthorizedClientService,
ClientRegistrationRepository clientRegistrationRepository) {
this.tokenType = tokenType;
this.header = header;
this.specifiedClientIds = specifiedClientIds;
this.oAuth2ClientProperties = oAuth2ClientProperties;
this.clientId = clientId;
this.oAuth2AuthorizedClientService = oAuth2AuthorizedClientService;
this.authorizedClientManager = new AuthorizedClientServiceOAuth2AuthorizedClientManager(
clientRegistrationRepository, this.oAuth2AuthorizedClientService);
clientRegistrationRepository, this.oAuth2AuthorizedClientService);
}
@Override
public void apply(RequestTemplate template) {
OAuth2AccessToken token = getToken(template);
String extractedToken = String.format("%s %s", tokenType, token.getTokenValue());
template.header(header);
template.header(header, extract(tokenType));
}
protected String extract(String tokenType) {
OAuth2AccessToken accessToken = getToken();
return String.format("%s %s", tokenType, accessToken.getTokenValue());
template.header(header, extractedToken);
}
public OAuth2AccessToken getToken() {
// if specific, try to use them to get token.
for (String clientId : this.specifiedClientIds) {
OAuth2AccessToken token = this.getToken(clientId);
public OAuth2AccessToken getToken(RequestTemplate template) {
// If specified, try to use them to get token.
if (StringUtils.hasText(clientId)) {
OAuth2AccessToken token = getToken(clientId);
if (token != null) {
return token;
}
}
// use clients from properties by default
for (String clientId : Optional.ofNullable(this.oAuth2ClientProperties)
.map(OAuth2ClientProperties::getRegistration).map(Map::keySet)
.orElse(new HashSet<>())) {
OAuth2AccessToken token = this.getToken(clientId);
if (token != null) {
return token;
}
// If not specified use host (synonymous with serviceId for load-balanced
// requests; non-load-balanced requests should use the method above).
OAuth2AccessToken token = getToken(getServiceId(template));
if (token != null) {
return token;
}
throw new IllegalStateException("No token acquired, which is illegal according to the contract.");
throw new IllegalStateException("OAuth2 token has not been successfully acquired.");
}
protected OAuth2AccessToken getToken(String clientId) {
if (!StringUtils.hasText(clientId)) {
return null;
}
@ -151,27 +139,35 @@ public class OAuth2AccessTokenInterceptor implements RequestInterceptor { @@ -151,27 +139,35 @@ public class OAuth2AccessTokenInterceptor implements RequestInterceptor {
principal = ANONYMOUS_AUTHENTICATION;
}
// already exist
// Already exist
OAuth2AuthorizedClient oAuth2AuthorizedClient = oAuth2AuthorizedClientService.loadAuthorizedClient(clientId,
principal.getName());
principal.getName());
if (oAuth2AuthorizedClient != null) {
OAuth2AccessToken accessToken = oAuth2AuthorizedClient.getAccessToken();
if (accessToken != null && this.noExpire(accessToken)) {
if (accessToken != null && notExpired(accessToken)) {
return accessToken;
}
}
OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(clientId)
.principal(principal).build();
OAuth2AuthorizedClient authorize = this.authorizedClientManager.authorize(authorizeRequest);
return Optional.ofNullable(authorize).map(OAuth2AuthorizedClient::getAccessToken)
.filter(this::noExpire)
.orElse(null);
.principal(principal).build();
OAuth2AuthorizedClient authorizedClient = authorizedClientManager.authorize(authorizeRequest);
return Optional.ofNullable(authorizedClient).map(OAuth2AuthorizedClient::getAccessToken)
.filter(this::notExpired).orElse(null);
}
protected boolean noExpire(OAuth2AccessToken token) {
protected boolean notExpired(OAuth2AccessToken token) {
return Optional.ofNullable(token).map(OAuth2AccessToken::getExpiresAt)
.map(expire -> expire.isAfter(Instant.now())).orElse(false);
.map(expire -> expire.isAfter(Instant.now())).orElse(false);
}
private static String getServiceId(RequestTemplate template) {
Target<?> feignTarget = template.feignTarget();
Assert.notNull(feignTarget, "feignTarget may not be null");
String url = feignTarget.url();
Assert.hasLength(url, "url may not be empty");
final URI originalUri = URI.create(url);
return originalUri.getHost();
}
}

6
spring-cloud-openfeign-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json

@ -79,6 +79,12 @@ @@ -79,6 +79,12 @@
"type": "java.lang.Boolean",
"description": "Enables load balancing for oauth2 access token provider.",
"defaultValue": "false"
},
{
"name": "feign.oauth2.registrationClientId",
"type": "java.lang.String",
"description": "Provides a clientId to be used with OAuth2.",
"defaultValue": ""
}
]
}

15
spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignAutoConfigurationTests.java

@ -17,8 +17,6 @@ @@ -17,8 +17,6 @@
package org.springframework.cloud.openfeign;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import feign.Target;
import org.assertj.core.api.Condition;
@ -141,17 +139,12 @@ class FeignAutoConfigurationTests { @@ -141,17 +139,12 @@ class FeignAutoConfigurationTests {
@Test
void shouldInstantiateFeignOAuth2FeignRequestInterceptor() {
runner.withPropertyValues("spring.cloud.openfeign.oauth2.enabled=true",
"spring.cloud.openfeign.oauth2.specifiedClientIds=feign-client")
"spring.cloud.openfeign.oauth2.clientId=feign-client")
.withBean(OAuth2AuthorizedClientService.class, () -> mock(OAuth2AuthorizedClientService.class))
.withBean(ClientRegistrationRepository.class, () -> mock(ClientRegistrationRepository.class))
.run(ctx -> {
assertOauth2AccessTokenInterceptorExists(ctx);
assertThatOauth2AccessTokenInterceptorHasSpecifiedIdsPropertyWithValue(ctx,
new ArrayList<String>() {
{
add("feign-client");
}
});
assertThatOauth2AccessTokenInterceptorHasSpecifiedIdsPropertyWithValue(ctx, "feign-client");
});
}
@ -184,9 +177,9 @@ class FeignAutoConfigurationTests { @@ -184,9 +177,9 @@ class FeignAutoConfigurationTests {
}
private void assertThatOauth2AccessTokenInterceptorHasSpecifiedIdsPropertyWithValue(
ConfigurableApplicationContext ctx, List<String> expectedValue) {
ConfigurableApplicationContext ctx, String expectedValue) {
final OAuth2AccessTokenInterceptor bean = ctx.getBean(OAuth2AccessTokenInterceptor.class);
assertThat(bean).hasFieldOrPropertyWithValue("specifiedClientIds", expectedValue);
assertThat(bean).hasFieldOrPropertyWithValue("clientId", expectedValue);
}
private void assertOnlyOneTargeterPresent(ConfigurableApplicationContext ctx, Class<?> beanClass) {

136
spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptorTests.java

@ -17,15 +17,14 @@ @@ -17,15 +17,14 @@
package org.springframework.cloud.openfeign.security;
import java.time.Instant;
import java.util.HashMap;
import feign.Request.HttpMethod;
import feign.RequestTemplate;
import org.assertj.core.api.Assertions;
import feign.Target;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties;
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;
@ -35,121 +34,117 @@ import org.springframework.security.oauth2.core.AuthorizationGrantType; @@ -35,121 +34,117 @@ import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.util.AlternativeJdkIdGenerator;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.BDDMockito.given;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Tests for {@link OAuth2AccessTokenInterceptor}.
*
* @author Dangzhicairang(小水牛)
* @author Olga Maciaszek-Sharma
*
*/
class OAuth2AccessTokenInterceptorTests {
private final OAuth2AuthorizedClientService mockOAuth2AuthorizedClientService = mock(
OAuth2AuthorizedClientService.class);
private final OAuth2AuthorizedClientManager mockOAuth2AuthorizedClientManager = mock(
OAuth2AuthorizedClientManager.class);
private OAuth2AccessTokenInterceptor oAuth2AccessTokenInterceptor;
private RequestTemplate requestTemplate;
private OAuth2ClientProperties mockOAuth2ClientProperties;
private static final String DEFAULT_CLIENT_ID = "feign-client";
@BeforeEach
void setUp() {
requestTemplate = new RequestTemplate().method(HttpMethod.GET);
mockOAuth2ClientProperties = mock(OAuth2ClientProperties.class);
given(mockOAuth2ClientProperties.getRegistration())
.willReturn(new HashMap<String, OAuth2ClientProperties.Registration>() {
{
put(DEFAULT_CLIENT_ID, mock(OAuth2ClientProperties.Registration.class));
}
});
Target<?> feignTarget = mock(Target.class);
when(feignTarget.url()).thenReturn("http://test");
requestTemplate.feignTarget(feignTarget);
}
@Test
void noTokenAcquired() {
OAuth2AuthorizedClientService mockOAuth2AuthorizedClientService = mock(OAuth2AuthorizedClientService.class);
given(mockOAuth2AuthorizedClientService.loadAuthorizedClient(anyString(), anyString())).willReturn(null);
oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2ClientProperties,
mockOAuth2AuthorizedClientService, mock(ClientRegistrationRepository.class));
OAuth2AuthorizedClientManager mockOAuth2AuthorizedClientManager = mock(OAuth2AuthorizedClientManager.class);
given(mockOAuth2AuthorizedClientManager.authorize(any())).willReturn(null);
void shouldThrowExceptionWhenNoTokenAcquired() {
when(mockOAuth2AuthorizedClientService.loadAuthorizedClient(anyString(), anyString())).thenReturn(null);
oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2AuthorizedClientService,
mock(ClientRegistrationRepository.class));
when(mockOAuth2AuthorizedClientManager.authorize(any())).thenReturn(null);
oAuth2AccessTokenInterceptor.setAuthorizedClientManager(mockOAuth2AuthorizedClientManager);
Assertions.assertThatExceptionOfType(IllegalStateException.class)
.isThrownBy(() -> oAuth2AccessTokenInterceptor.apply(requestTemplate))
.withMessage("No token acquired, which is illegal according to the contract.");
assertThatExceptionOfType(IllegalStateException.class)
.isThrownBy(() -> oAuth2AccessTokenInterceptor.apply(requestTemplate))
.withMessage("OAuth2 token has not been successfully acquired.");
}
@Test
void validTokenAcquired() {
OAuth2AuthorizedClientService mockOAuth2AuthorizedClientService = mock(OAuth2AuthorizedClientService.class);
given(mockOAuth2AuthorizedClientService.loadAuthorizedClient(anyString(), anyString())).willReturn(null);
oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2ClientProperties,
mockOAuth2AuthorizedClientService, mock(ClientRegistrationRepository.class));
OAuth2AuthorizedClientManager mockOAuth2AuthorizedClientManager = mock(OAuth2AuthorizedClientManager.class);
given(mockOAuth2AuthorizedClientManager.authorize(any())).willReturn(validTokenOAuth2AuthorizedClient());
void shouldAcquireValidToken() {
when(mockOAuth2AuthorizedClientService.loadAuthorizedClient(anyString(), anyString())).thenReturn(null);
oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2AuthorizedClientService,
mock(ClientRegistrationRepository.class));
when(mockOAuth2AuthorizedClientManager.authorize(
argThat((OAuth2AuthorizeRequest request) -> ("test").equals(request.getClientRegistrationId()))))
.thenReturn(validTokenOAuth2AuthorizedClient());
oAuth2AccessTokenInterceptor.setAuthorizedClientManager(mockOAuth2AuthorizedClientManager);
oAuth2AccessTokenInterceptor.apply(requestTemplate);
Assertions.assertThat(requestTemplate.headers().get("Authorization"))
.contains("Bearer Valid Token");
assertThat(requestTemplate.headers().get("Authorization")).contains("Bearer Valid Token");
}
@Test
void expireTokenAcquired() {
OAuth2AuthorizedClientService mockOAuth2AuthorizedClientService = mock(OAuth2AuthorizedClientService.class);
given(mockOAuth2AuthorizedClientService.loadAuthorizedClient(anyString(), anyString())).willReturn(null);
void shouldThrowExceptionWhenExpiredTokenAcquired() {
when(mockOAuth2AuthorizedClientService.loadAuthorizedClient(anyString(), anyString())).thenReturn(null);
oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2AuthorizedClientService,
mock(ClientRegistrationRepository.class));
when(mockOAuth2AuthorizedClientManager.authorize(any())).thenReturn(expiredTokenOAuth2AuthorizedClient());
oAuth2AccessTokenInterceptor.setAuthorizedClientManager(mockOAuth2AuthorizedClientManager);
oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2ClientProperties,
mockOAuth2AuthorizedClientService, mock(ClientRegistrationRepository.class));
assertThatExceptionOfType(IllegalStateException.class)
.isThrownBy(() -> oAuth2AccessTokenInterceptor.apply(requestTemplate))
.withMessage("OAuth2 token has not been successfully acquired.");
}
OAuth2AuthorizedClientManager mockOAuth2AuthorizedClientManager = mock(OAuth2AuthorizedClientManager.class);
given(mockOAuth2AuthorizedClientManager.authorize(any())).willReturn(expiredTokenOAuth2AuthorizedClient());
@Test
void shouldAcquireTokenFromAuthorizedClient() {
when(mockOAuth2AuthorizedClientService.loadAuthorizedClient(eq("test"), anyString()))
.thenReturn(validTokenOAuth2AuthorizedClient());
oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2AuthorizedClientService,
mock(ClientRegistrationRepository.class));
oAuth2AccessTokenInterceptor.setAuthorizedClientManager(mockOAuth2AuthorizedClientManager);
oAuth2AccessTokenInterceptor.apply(requestTemplate);
Assertions.assertThatExceptionOfType(IllegalStateException.class)
.isThrownBy(() -> oAuth2AccessTokenInterceptor.apply(requestTemplate))
.withMessage("No token acquired, which is illegal according to the contract.");
assertThat(requestTemplate.headers().get("Authorization")).contains("Bearer Valid Token");
}
@Test
void acquireTokenFromAuthorizedClient() {
OAuth2AuthorizedClientService mockOAuth2AuthorizedClientService = mock(OAuth2AuthorizedClientService.class);
given(mockOAuth2AuthorizedClientService.loadAuthorizedClient(anyString(), anyString()))
.willReturn(validTokenOAuth2AuthorizedClient());
oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2ClientProperties,
mockOAuth2AuthorizedClientService, mock(ClientRegistrationRepository.class));
void shouldAcquireValidTokenFromSpecifiedClientId() {
when(mockOAuth2AuthorizedClientService.loadAuthorizedClient(eq("testId"), anyString()))
.thenReturn(validTokenOAuth2AuthorizedClient());
oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor("testId", mockOAuth2AuthorizedClientService,
mock(ClientRegistrationRepository.class));
oAuth2AccessTokenInterceptor.apply(requestTemplate);
Assertions.assertThat(requestTemplate.headers().get("Authorization"))
.contains("Bearer Valid Token");
assertThat(requestTemplate.headers().get("Authorization")).contains("Bearer Valid Token");
}
private OAuth2AccessToken validToken() {
return new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "Valid Token", Instant.now(),
Instant.now().plusSeconds(60L));
Instant.now().plusSeconds(60L));
}
private OAuth2AccessToken expiredToken() {
return new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "Expired Token",
Instant.now().minusSeconds(61L), Instant.now().minusSeconds(60L));
Instant.now().minusSeconds(61L), Instant.now().minusSeconds(60L));
}
private OAuth2AuthorizedClient validTokenOAuth2AuthorizedClient() {
@ -161,10 +156,9 @@ class OAuth2AccessTokenInterceptorTests { @@ -161,10 +156,9 @@ class OAuth2AccessTokenInterceptorTests {
}
private ClientRegistration defaultClientRegistration() {
return ClientRegistration.withRegistrationId(new AlternativeJdkIdGenerator().generateId()
.toString())
.clientId(DEFAULT_CLIENT_ID).tokenUri("mock token uri")
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS).build();
return ClientRegistration.withRegistrationId(new AlternativeJdkIdGenerator().generateId().toString())
.clientId(DEFAULT_CLIENT_ID).tokenUri("mock token uri")
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS).build();
}
}

2
spring-cloud-openfeign-dependencies/pom.xml

@ -19,7 +19,6 @@ @@ -19,7 +19,6 @@
<feign-form.version>3.8.0</feign-form.version>
<!-- Deprecated - reached EOL -->
<spring-security-oauth2-autoconfigure.version>2.5.2</spring-security-oauth2-autoconfigure.version>
<spring-security-oauth2-client.version>6.0.0-SNAPSHOT</spring-security-oauth2-client.version>
</properties>
<dependencyManagement>
<dependencies>
@ -31,7 +30,6 @@ @@ -31,7 +30,6 @@
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
<version>${spring-security-oauth2-client.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>

Loading…
Cancel
Save