diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml
index edab0071..cdb4d588 100644
--- a/spring-cloud-openfeign-core/pom.xml
+++ b/spring-cloud-openfeign-core/pom.xml
@@ -119,6 +119,11 @@
okhttp
true
+
+ org.springframework.security.oauth.boot
+ spring-security-oauth2-autoconfigure
+ true
+
org.springframework.boot
spring-boot-autoconfigure-processor
diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java
index f48ecee5..93e8ca6e 100644
--- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java
+++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java
@@ -27,6 +27,7 @@ import javax.annotation.PreDestroy;
import feign.Client;
import feign.Feign;
+import feign.RequestInterceptor;
import feign.httpclient.ApacheHttpClient;
import feign.okhttp.OkHttpClient;
import okhttp3.ConnectionPool;
@@ -39,6 +40,7 @@ import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.beans.factory.annotation.Autowired;
+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;
@@ -48,11 +50,14 @@ import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionMa
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory;
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
+import org.springframework.cloud.openfeign.security.OAuth2FeignRequestInterceptor;
import org.springframework.cloud.openfeign.support.DefaultGzipDecoderConfiguration;
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
+import org.springframework.security.oauth2.client.OAuth2ClientContext;
+import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
/**
* @author Spencer Gibb
@@ -209,4 +214,19 @@ public class FeignAutoConfiguration {
}
+ @Configuration(proxyBeanMethods = false)
+ @ConditionalOnClass(OAuth2ClientContext.class)
+ @ConditionalOnProperty("feign.oauth2.enabled")
+ protected static class Oauth2FeignConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean(OAuth2FeignRequestInterceptor.class)
+ @ConditionalOnBean({ OAuth2ClientContext.class, OAuth2ProtectedResourceDetails.class })
+ public RequestInterceptor oauth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext,
+ OAuth2ProtectedResourceDetails resource) {
+ return new OAuth2FeignRequestInterceptor(oAuth2ClientContext, resource);
+ }
+
+ }
+
}
diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2FeignRequestInterceptor.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2FeignRequestInterceptor.java
new file mode 100644
index 00000000..0389e7cf
--- /dev/null
+++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2FeignRequestInterceptor.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2015-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.openfeign.security;
+
+import java.util.Arrays;
+
+import feign.RequestInterceptor;
+import feign.RequestTemplate;
+
+import org.springframework.security.oauth2.client.OAuth2ClientContext;
+import org.springframework.security.oauth2.client.http.AccessTokenRequiredException;
+import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
+import org.springframework.security.oauth2.client.resource.UserRedirectRequiredException;
+import org.springframework.security.oauth2.client.token.AccessTokenProvider;
+import org.springframework.security.oauth2.client.token.AccessTokenProviderChain;
+import org.springframework.security.oauth2.client.token.AccessTokenRequest;
+import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider;
+import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider;
+import org.springframework.security.oauth2.client.token.grant.implicit.ImplicitAccessTokenProvider;
+import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordAccessTokenProvider;
+import org.springframework.security.oauth2.common.OAuth2AccessToken;
+
+/**
+ * Pre-defined custom RequestInterceptor for Feign Requests. It uses the
+ * {@link OAuth2ClientContext OAuth2ClientContext} provided from the environment and
+ * construct a new header on the request before it is made by Feign.
+ *
+ * @author Joao Pedro Evangelista
+ * @author Tim Ysewyn
+ * @since 3.0.0
+ */
+public class OAuth2FeignRequestInterceptor implements RequestInterceptor {
+
+ /**
+ * The name of the token.
+ */
+ public static final String BEARER = "Bearer";
+
+ /**
+ * The name of the header.
+ */
+ public static final String AUTHORIZATION = "Authorization";
+
+ private final OAuth2ClientContext oAuth2ClientContext;
+
+ private final OAuth2ProtectedResourceDetails resource;
+
+ private final String tokenType;
+
+ private final String header;
+
+ private AccessTokenProvider accessTokenProvider = new AccessTokenProviderChain(Arrays.asList(
+ new AuthorizationCodeAccessTokenProvider(), new ImplicitAccessTokenProvider(),
+ new ResourceOwnerPasswordAccessTokenProvider(), new ClientCredentialsAccessTokenProvider()));
+
+ /**
+ * Default constructor which uses the provided OAuth2ClientContext and Bearer tokens
+ * within Authorization header.
+ * @param oAuth2ClientContext provided context
+ * @param resource type of resource to be accessed
+ */
+ public OAuth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext,
+ OAuth2ProtectedResourceDetails resource) {
+ this(oAuth2ClientContext, resource, BEARER, AUTHORIZATION);
+ }
+
+ /**
+ * Fully customizable constructor for changing token type and header name, in cases of
+ * Bearer and Authorization is not the default such as "bearer", "authorization".
+ * @param oAuth2ClientContext current oAuth2 Context
+ * @param resource type of resource to be accessed
+ * @param tokenType type of token e.g. "token", "Bearer"
+ * @param header name of the header e.g. "Authorization", "authorization"
+ */
+ public OAuth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext,
+ OAuth2ProtectedResourceDetails resource, String tokenType, String header) {
+ this.oAuth2ClientContext = oAuth2ClientContext;
+ this.resource = resource;
+ this.tokenType = tokenType;
+ this.header = header;
+ }
+
+ /**
+ * Create a template with the header of provided name and extracted extract.
+ *
+ * @see RequestInterceptor#apply(RequestTemplate)
+ */
+ @Override
+ public void apply(RequestTemplate template) {
+ template.header(header); // Clears out the header, no "clear" method available.
+ template.header(header, extract(tokenType));
+ }
+
+ /**
+ * Extracts the token extract id the access token exists or returning an empty extract
+ * if there is no one on the context it may occasionally causes Unauthorized response
+ * since the token extract is empty.
+ * @param tokenType type name of token
+ * @return token value from context if it exists otherwise empty String
+ */
+ protected String extract(String tokenType) {
+ OAuth2AccessToken accessToken = getToken();
+ return String.format("%s %s", tokenType, accessToken.getValue());
+ }
+
+ /**
+ * Extract the access token within the request or try to acquire a new one by
+ * delegating it to {@link #acquireAccessToken()}.
+ * @return valid token
+ */
+ public OAuth2AccessToken getToken() {
+
+ OAuth2AccessToken accessToken = oAuth2ClientContext.getAccessToken();
+ if (accessToken == null || accessToken.isExpired()) {
+ try {
+ accessToken = acquireAccessToken();
+ }
+ catch (UserRedirectRequiredException e) {
+ oAuth2ClientContext.setAccessToken(null);
+ String stateKey = e.getStateKey();
+ if (stateKey != null) {
+ Object stateToPreserve = e.getStateToPreserve();
+ if (stateToPreserve == null) {
+ stateToPreserve = "NONE";
+ }
+ oAuth2ClientContext.setPreservedState(stateKey, stateToPreserve);
+ }
+ throw e;
+ }
+ }
+ return accessToken;
+ }
+
+ /**
+ * Try to acquire the token using a access token provider.
+ * @return valid access token
+ * @throws UserRedirectRequiredException in case the user needs to be redirected to an
+ * approval page or login page
+ */
+ protected OAuth2AccessToken acquireAccessToken() throws UserRedirectRequiredException {
+ AccessTokenRequest tokenRequest = oAuth2ClientContext.getAccessTokenRequest();
+ if (tokenRequest == null) {
+ throw new AccessTokenRequiredException(
+ "Cannot find valid context on request for resource '" + resource.getId() + "'.", resource);
+ }
+ String stateKey = tokenRequest.getStateKey();
+ if (stateKey != null) {
+ tokenRequest.setPreservedState(oAuth2ClientContext.removePreservedState(stateKey));
+ }
+ OAuth2AccessToken existingToken = oAuth2ClientContext.getAccessToken();
+ if (existingToken != null) {
+ oAuth2ClientContext.setAccessToken(existingToken);
+ }
+ OAuth2AccessToken obtainableAccessToken;
+ obtainableAccessToken = accessTokenProvider.obtainAccessToken(resource, tokenRequest);
+ if (obtainableAccessToken == null || obtainableAccessToken.getValue() == null) {
+ throw new IllegalStateException(
+ " Access token provider returned a null token, which is illegal according to the contract.");
+ }
+ oAuth2ClientContext.setAccessToken(obtainableAccessToken);
+ return obtainableAccessToken;
+ }
+
+ public void setAccessTokenProvider(AccessTokenProvider accessTokenProvider) {
+ this.accessTokenProvider = accessTokenProvider;
+ }
+
+}
diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/MockAccessTokenProvider.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/MockAccessTokenProvider.java
new file mode 100644
index 00000000..3f8e4e2d
--- /dev/null
+++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/MockAccessTokenProvider.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2015-2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.openfeign.security;
+
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
+import org.springframework.security.oauth2.client.resource.UserApprovalRequiredException;
+import org.springframework.security.oauth2.client.resource.UserRedirectRequiredException;
+import org.springframework.security.oauth2.client.token.AccessTokenProvider;
+import org.springframework.security.oauth2.client.token.AccessTokenRequest;
+import org.springframework.security.oauth2.common.OAuth2AccessToken;
+import org.springframework.security.oauth2.common.OAuth2RefreshToken;
+
+/**
+ * Mocks the access token provider
+ *
+ * @author Mihhail Verhovtsov
+ */
+public class MockAccessTokenProvider implements AccessTokenProvider {
+
+ private OAuth2AccessToken token;
+
+ public MockAccessTokenProvider(OAuth2AccessToken token) {
+ this.token = token;
+ }
+
+ @Override
+ public OAuth2AccessToken obtainAccessToken(OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails,
+ AccessTokenRequest accessTokenRequest)
+ throws UserRedirectRequiredException, UserApprovalRequiredException, AccessDeniedException {
+ return token;
+ }
+
+ @Override
+ public boolean supportsResource(OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails) {
+ return true;
+ }
+
+ @Override
+ public OAuth2AccessToken refreshAccessToken(OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails,
+ OAuth2RefreshToken oAuth2RefreshToken, AccessTokenRequest accessTokenRequest)
+ throws UserRedirectRequiredException {
+ return null;
+ }
+
+ @Override
+ public boolean supportsRefresh(OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails) {
+ return false;
+ }
+
+}
diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/MockOAuth2AccessToken.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/MockOAuth2AccessToken.java
new file mode 100644
index 00000000..da272ff2
--- /dev/null
+++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/MockOAuth2AccessToken.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2015-2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.openfeign.security;
+
+import java.util.Date;
+import java.util.Map;
+import java.util.Set;
+
+import org.springframework.security.oauth2.common.OAuth2AccessToken;
+import org.springframework.security.oauth2.common.OAuth2RefreshToken;
+
+/**
+ * Mocks the OAuth2 access token
+ *
+ * @author Mihhail Verhovtsov
+ */
+public class MockOAuth2AccessToken implements OAuth2AccessToken {
+
+ private String value;
+
+ public MockOAuth2AccessToken(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public Map getAdditionalInformation() {
+ return null;
+ }
+
+ @Override
+ public Set getScope() {
+ return null;
+ }
+
+ @Override
+ public OAuth2RefreshToken getRefreshToken() {
+ return null;
+ }
+
+ @Override
+ public String getTokenType() {
+ return null;
+ }
+
+ @Override
+ public boolean isExpired() {
+ return false;
+ }
+
+ @Override
+ public Date getExpiration() {
+ return null;
+ }
+
+ @Override
+ public int getExpiresIn() {
+ return 0;
+ }
+
+ @Override
+ public String getValue() {
+ return value;
+ }
+
+}
diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/MockOAuth2ClientContext.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/MockOAuth2ClientContext.java
new file mode 100644
index 00000000..237642a9
--- /dev/null
+++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/MockOAuth2ClientContext.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2015-2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.openfeign.security;
+
+import java.util.HashMap;
+
+import org.springframework.security.oauth2.client.OAuth2ClientContext;
+import org.springframework.security.oauth2.client.token.AccessTokenRequest;
+import org.springframework.security.oauth2.client.token.DefaultAccessTokenRequest;
+import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
+import org.springframework.security.oauth2.common.OAuth2AccessToken;
+
+/**
+ * Mocks the current client context
+ *
+ * @author João Pedro Evangelista
+ */
+final class MockOAuth2ClientContext implements OAuth2ClientContext {
+
+ private final String value;
+
+ MockOAuth2ClientContext(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public OAuth2AccessToken getAccessToken() {
+ return new DefaultOAuth2AccessToken(value);
+ }
+
+ @Override
+ public void setAccessToken(OAuth2AccessToken accessToken) {
+
+ }
+
+ @Override
+ public AccessTokenRequest getAccessTokenRequest() {
+ DefaultAccessTokenRequest tokenRequest = new DefaultAccessTokenRequest(new HashMap());
+ tokenRequest.setExistingToken(new DefaultOAuth2AccessToken(value));
+ return tokenRequest;
+ }
+
+ @Override
+ public void setPreservedState(String stateKey, Object preservedState) {
+
+ }
+
+ @Override
+ public Object removePreservedState(String stateKey) {
+ return null;
+ }
+
+}
diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/OAuth2FeignRequestInterceptorTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/OAuth2FeignRequestInterceptorTests.java
new file mode 100644
index 00000000..b0c06f41
--- /dev/null
+++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/OAuth2FeignRequestInterceptorTests.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2015-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.openfeign.security;
+
+import java.util.Collection;
+import java.util.Map;
+
+import feign.Request.HttpMethod;
+import feign.RequestTemplate;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
+import org.springframework.security.oauth2.client.OAuth2ClientContext;
+import org.springframework.security.oauth2.client.resource.BaseOAuth2ProtectedResourceDetails;
+import org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException;
+import org.springframework.security.oauth2.client.token.AccessTokenRequest;
+import org.springframework.security.oauth2.common.OAuth2AccessToken;
+
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.hasSize;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * @author João Pedro Evangelista
+ * @author Tim Ysewyn
+ */
+public class OAuth2FeignRequestInterceptorTests {
+
+ private OAuth2FeignRequestInterceptor oAuth2FeignRequestInterceptor;
+
+ private RequestTemplate requestTemplate;
+
+ @Before
+ public void setUp() {
+ oAuth2FeignRequestInterceptor = new OAuth2FeignRequestInterceptor(new MockOAuth2ClientContext("Fancy"),
+ new BaseOAuth2ProtectedResourceDetails());
+ requestTemplate = new RequestTemplate().method(HttpMethod.GET);
+ }
+
+ @Test
+ public void applyAuthorizationHeader() {
+ oAuth2FeignRequestInterceptor.apply(requestTemplate);
+ Map> headers = requestTemplate.headers();
+ Assert.assertTrue("RequestTemplate must have a Authorization header", headers.containsKey("Authorization"));
+ Assert.assertThat("Authorization must have a extract of Fancy", headers.get("Authorization"),
+ contains("Bearer Fancy"));
+ }
+
+ @Test(expected = OAuth2AccessDeniedException.class)
+ public void tryToAcquireToken() {
+ oAuth2FeignRequestInterceptor = new OAuth2FeignRequestInterceptor(new DefaultOAuth2ClientContext(),
+ new BaseOAuth2ProtectedResourceDetails());
+ OAuth2AccessToken oAuth2AccessToken = oAuth2FeignRequestInterceptor.getToken();
+ Assert.assertTrue(oAuth2AccessToken.getValue() + " Must be null", oAuth2AccessToken.getValue() == null);
+ }
+
+ @Test
+ public void configureAccessTokenProvider() {
+ OAuth2AccessToken mockedToken = new MockOAuth2AccessToken("MOCKED_TOKEN");
+ oAuth2FeignRequestInterceptor.setAccessTokenProvider(new MockAccessTokenProvider(mockedToken));
+ Assert.assertEquals("Should return same mocked token instance", mockedToken,
+ oAuth2FeignRequestInterceptor.acquireAccessToken());
+ }
+
+ @Test
+ public void applyAuthorizationHeaderOnlyOnce() {
+ OAuth2ClientContext oAuth2ClientContext = mock(OAuth2ClientContext.class);
+ when(oAuth2ClientContext.getAccessToken()).thenReturn(new MockOAuth2AccessToken("MOCKED_TOKEN"));
+
+ OAuth2FeignRequestInterceptor oAuth2FeignRequestInterceptor = new OAuth2FeignRequestInterceptor(
+ oAuth2ClientContext, new BaseOAuth2ProtectedResourceDetails());
+
+ oAuth2FeignRequestInterceptor.apply(requestTemplate);
+
+ // First idempotent call failed, retry mechanism kicks in, and token has expired
+ // in the meantime
+
+ OAuth2AccessToken expiredAccessToken = mock(OAuth2AccessToken.class);
+ when(expiredAccessToken.isExpired()).thenReturn(true);
+ when(oAuth2ClientContext.getAccessToken()).thenReturn(expiredAccessToken);
+ AccessTokenRequest accessTokenRequest = mock(AccessTokenRequest.class);
+ when(oAuth2ClientContext.getAccessTokenRequest()).thenReturn(accessTokenRequest);
+ OAuth2AccessToken newToken = new MockOAuth2AccessToken("Fancy");
+ oAuth2FeignRequestInterceptor.setAccessTokenProvider(new MockAccessTokenProvider(newToken));
+
+ oAuth2FeignRequestInterceptor.apply(requestTemplate);
+
+ Map> headers = requestTemplate.headers();
+ Assert.assertTrue("RequestTemplate must have a Authorization header", headers.containsKey("Authorization"));
+ Assert.assertThat("Authorization must have a extract of Fancy", headers.get("Authorization"), hasSize(1));
+ Assert.assertThat("Authorization must have a extract of Fancy", headers.get("Authorization"),
+ contains("Bearer Fancy"));
+ }
+
+}
diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml
index aa0ddac1..dc948b01 100644
--- a/spring-cloud-openfeign-dependencies/pom.xml
+++ b/spring-cloud-openfeign-dependencies/pom.xml
@@ -17,9 +17,15 @@
10.10.1
3.8.0
+ 2.1.2.RELEASE
+
+ org.springframework.security.oauth.boot
+ spring-security-oauth2-autoconfigure
+ ${spring-security-oauth2-autoconfigure.version}
+
org.springframework.cloud
spring-cloud-openfeign-core