gh-631 - Add support for customizing OAuth2FeignReqeustInterceptor.accessTokenProvider (injecting e.g. LoadBalancerInterceptor) (#642)
This commit is contained in:
@@ -55,11 +55,14 @@ import org.springframework.cache.interceptor.CacheInterceptor;
|
||||
import org.springframework.cloud.client.actuator.HasFeatures;
|
||||
import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
|
||||
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor;
|
||||
import org.springframework.cloud.client.loadbalancer.RetryLoadBalancerInterceptor;
|
||||
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
|
||||
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.security.OAuth2FeignRequestInterceptorConfigurer;
|
||||
import org.springframework.cloud.openfeign.support.FeignEncoderProperties;
|
||||
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
|
||||
import org.springframework.cloud.openfeign.support.PageJacksonModule;
|
||||
@@ -73,6 +76,8 @@ import org.springframework.data.domain.Sort;
|
||||
import org.springframework.security.oauth2.client.OAuth2ClientContext;
|
||||
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
|
||||
|
||||
import static org.springframework.cloud.openfeign.security.OAuth2FeignRequestInterceptorBuilder.buildWithConfigurers;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Julien Roy
|
||||
@@ -84,6 +89,7 @@ import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResour
|
||||
* @author Andrii Bohutskyi
|
||||
* @author Kwangyong Kim
|
||||
* @author Sam Kruglov
|
||||
* @author Wojciech Mąka
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(Feign.class)
|
||||
@@ -322,12 +328,30 @@ public class FeignAutoConfiguration {
|
||||
@ConditionalOnProperty("feign.oauth2.enabled")
|
||||
protected static class Oauth2FeignConfiguration {
|
||||
|
||||
@ConditionalOnBean({ RetryLoadBalancerInterceptor.class, OAuth2ClientContext.class,
|
||||
OAuth2ProtectedResourceDetails.class })
|
||||
@ConditionalOnProperty(value = "feign.oauth2.load-balanced", havingValue = "true")
|
||||
@Bean
|
||||
public OAuth2FeignRequestInterceptorConfigurer retryLoadBalancerInterceptorInjectingConfigurer(
|
||||
final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
|
||||
return builder -> builder.withAccessTokenProviderInterceptors(loadBalancerInterceptor);
|
||||
}
|
||||
|
||||
@ConditionalOnBean({ LoadBalancerInterceptor.class, OAuth2ClientContext.class,
|
||||
OAuth2ProtectedResourceDetails.class })
|
||||
@ConditionalOnProperty(value = "feign.oauth2.load-balanced", havingValue = "true")
|
||||
@Bean
|
||||
public OAuth2FeignRequestInterceptorConfigurer loadBalancerInterceptorInjectingConfigurer(
|
||||
final LoadBalancerInterceptor loadBalancerInterceptor) {
|
||||
return builder -> builder.withAccessTokenProviderInterceptors(loadBalancerInterceptor);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(OAuth2FeignRequestInterceptor.class)
|
||||
@ConditionalOnBean({ OAuth2ClientContext.class, OAuth2ProtectedResourceDetails.class })
|
||||
public RequestInterceptor oauth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext,
|
||||
OAuth2ProtectedResourceDetails resource) {
|
||||
return new OAuth2FeignRequestInterceptor(oAuth2ClientContext, resource);
|
||||
OAuth2ProtectedResourceDetails resource, List<OAuth2FeignRequestInterceptorConfigurer> configurers) {
|
||||
return buildWithConfigurers(oAuth2ClientContext, resource, configurers);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2015-2021 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.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.security.oauth2.client.OAuth2ClientContext;
|
||||
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
|
||||
import org.springframework.security.oauth2.client.token.AccessTokenProvider;
|
||||
import org.springframework.security.oauth2.client.token.AccessTokenProviderChain;
|
||||
import org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Allows to customize pre-defined {@link OAuth2FeignRequestInterceptor} using configurer
|
||||
* beans of class {@link OAuth2FeignRequestInterceptorConfigurer}. Each configurer
|
||||
* instance can add {@link AccessTokenProvider} new {@link ClientHttpRequestInterceptor}
|
||||
* instances.
|
||||
*
|
||||
* @author Wojciech Mąka
|
||||
* @since 3.1.1
|
||||
*/
|
||||
public class OAuth2FeignRequestInterceptorBuilder {
|
||||
|
||||
private AccessTokenProvider accessTokenProvider;
|
||||
|
||||
private final List<ClientHttpRequestInterceptor> accessTokenProviderInterceptors = new ArrayList<>();
|
||||
|
||||
public OAuth2FeignRequestInterceptorBuilder() {
|
||||
accessTokenProvider = new AccessTokenProviderChain(Arrays.<AccessTokenProvider>asList(
|
||||
new AuthorizationCodeAccessTokenProvider(), new ImplicitAccessTokenProvider(),
|
||||
new ResourceOwnerPasswordAccessTokenProvider(), new ClientCredentialsAccessTokenProvider()));
|
||||
}
|
||||
|
||||
public OAuth2FeignRequestInterceptorBuilder withAccessTokenProviderInterceptors(
|
||||
ClientHttpRequestInterceptor... interceptors) {
|
||||
accessTokenProviderInterceptors.addAll(Arrays.asList(interceptors));
|
||||
return this;
|
||||
}
|
||||
|
||||
OAuth2FeignRequestInterceptor build(OAuth2ClientContext oAuth2ClientContext,
|
||||
OAuth2ProtectedResourceDetails resource) {
|
||||
if (OAuth2AccessTokenSupport.class.isAssignableFrom(accessTokenProvider.getClass())) {
|
||||
((OAuth2AccessTokenSupport) accessTokenProvider).setInterceptors(accessTokenProviderInterceptors);
|
||||
}
|
||||
final OAuth2FeignRequestInterceptor feignRequestInterceptor = new OAuth2FeignRequestInterceptor(
|
||||
oAuth2ClientContext, resource);
|
||||
feignRequestInterceptor.setAccessTokenProvider(accessTokenProvider);
|
||||
return feignRequestInterceptor;
|
||||
}
|
||||
|
||||
public static OAuth2FeignRequestInterceptor buildWithConfigurers(OAuth2ClientContext oAuth2ClientContext,
|
||||
OAuth2ProtectedResourceDetails resource, List<OAuth2FeignRequestInterceptorConfigurer> buildConfigurers) {
|
||||
final OAuth2FeignRequestInterceptorBuilder builder = new OAuth2FeignRequestInterceptorBuilder();
|
||||
for (OAuth2FeignRequestInterceptorConfigurer configurer : buildConfigurers) {
|
||||
configurer.customize(builder);
|
||||
}
|
||||
return builder.build(oAuth2ClientContext, resource);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 org.springframework.security.oauth2.client.token.AccessTokenProvider;
|
||||
|
||||
/**
|
||||
* Interface for configurer beans working with
|
||||
* {@link OAuth2FeignRequestInterceptorBuilder} in order to provide custom interceptors
|
||||
* for {@link AccessTokenProvider} managed internally by
|
||||
* {@link OAuth2FeignRequestInterceptor}.
|
||||
*
|
||||
* @author Wojciech Mąka
|
||||
* @since 3.1.1
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface OAuth2FeignRequestInterceptorConfigurer {
|
||||
|
||||
void customize(OAuth2FeignRequestInterceptorBuilder requestInterceptorBuilder);
|
||||
|
||||
}
|
||||
@@ -61,6 +61,18 @@
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Enables options value refresh capability for Feign.",
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"name": "feign.oauth2.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Enables feign interceptor for managing oauth2 access token.",
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"name": "feign.oauth2.load-balanced",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Enables load balancing for oauth2 access token provider.",
|
||||
"defaultValue": "false"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -23,10 +23,19 @@ import org.assertj.core.api.Condition;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor;
|
||||
import org.springframework.cloud.openfeign.FeignAutoConfiguration.CircuitBreakerPresentFeignTargeterConfiguration.DefaultCircuitBreakerNameResolver;
|
||||
import org.springframework.cloud.openfeign.security.MockOAuth2ClientContext;
|
||||
import org.springframework.cloud.openfeign.security.OAuth2FeignRequestInterceptor;
|
||||
import org.springframework.cloud.openfeign.security.OAuth2FeignRequestInterceptorBuilder;
|
||||
import org.springframework.cloud.openfeign.security.OAuth2FeignRequestInterceptorConfigurer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.support.BasicAuthenticationInterceptor;
|
||||
import org.springframework.security.oauth2.client.resource.BaseOAuth2ProtectedResourceDetails;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -36,6 +45,7 @@ import static org.mockito.Mockito.mock;
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @author Andrii Bohutskyi
|
||||
* @author Kwangyong Kim
|
||||
* @author Wojciech Mąka
|
||||
*/
|
||||
class FeignAutoConfigurationTests {
|
||||
|
||||
@@ -81,6 +91,66 @@ class FeignAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldInstantiateFeignOAuth2FeignRequestInterceptorWithoutInterceptors() {
|
||||
runner.withPropertyValues("feign.oauth2.enabled=true").withBean(MockOAuth2ClientContext.class, "token")
|
||||
.withBean(BaseOAuth2ProtectedResourceDetails.class)
|
||||
.withBean(LoadBalancerInterceptor.class, () -> mock(LoadBalancerInterceptor.class)).run(ctx -> {
|
||||
assertOauth2FeignRequestInterceptorExists(ctx);
|
||||
assertAccessTokenProviderInterceptorNotExists(ctx, LoadBalancerInterceptor.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldInstantiateFeignOAuth2FeignRequestInterceptorWithLoadBalancedInterceptor() {
|
||||
runner.withPropertyValues("feign.oauth2.enabled=true", "feign.oauth2.load-balanced=true")
|
||||
.withBean(MockOAuth2ClientContext.class, "token").withBean(BaseOAuth2ProtectedResourceDetails.class)
|
||||
.withBean(LoadBalancerInterceptor.class, () -> mock(LoadBalancerInterceptor.class)).run(ctx -> {
|
||||
assertOauth2FeignRequestInterceptorExists(ctx);
|
||||
assertAccessTokenProviderInterceptorExists(ctx, LoadBalancerInterceptor.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldInstantiateFeignOAuth2FeignRequestInterceptorWithoutLoadBalancedInterceptorIfNoBeanPresent() {
|
||||
runner.withPropertyValues("feign.oauth2.enabled=true", "feign.oauth2.load-balanced=true")
|
||||
.withBean(MockOAuth2ClientContext.class, "token").withBean(BaseOAuth2ProtectedResourceDetails.class)
|
||||
.run(ctx -> {
|
||||
assertOauth2FeignRequestInterceptorExists(ctx);
|
||||
assertAccessTokenProviderInterceptorNotExists(ctx, LoadBalancerInterceptor.class);
|
||||
});
|
||||
}
|
||||
|
||||
@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);
|
||||
});
|
||||
}
|
||||
|
||||
private void assertOauth2FeignRequestInterceptorExists(ConfigurableApplicationContext ctx) {
|
||||
AssertableApplicationContext context = AssertableApplicationContext.get(() -> ctx);
|
||||
assertThat(context).hasSingleBean(OAuth2FeignRequestInterceptor.class);
|
||||
}
|
||||
|
||||
private void assertAccessTokenProviderInterceptorExists(ConfigurableApplicationContext ctx,
|
||||
Class<? extends ClientHttpRequestInterceptor> clazz) {
|
||||
AssertableApplicationContext context = AssertableApplicationContext.get(() -> ctx);
|
||||
assertThat(context).getBean(OAuth2FeignRequestInterceptor.class).extracting("accessTokenProvider")
|
||||
.extracting("interceptors").asList().first().isInstanceOf(clazz);
|
||||
}
|
||||
|
||||
private void assertAccessTokenProviderInterceptorNotExists(ConfigurableApplicationContext ctx,
|
||||
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();
|
||||
}
|
||||
|
||||
private void assertOnlyOneTargeterPresent(ConfigurableApplicationContext ctx, Class<?> beanClass) {
|
||||
assertThat(ctx.getBeansOfType(Targeter.class)).hasSize(1).hasValueSatisfying(new Condition<>(
|
||||
beanClass::isInstance, String.format("Targeter should be an instance of %s", beanClass)));
|
||||
@@ -108,4 +178,14 @@ class FeignAutoConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
static class CustomOAuth2FeignRequestInterceptorConfigurer implements OAuth2FeignRequestInterceptorConfigurer {
|
||||
|
||||
@Override
|
||||
public void customize(OAuth2FeignRequestInterceptorBuilder requestInterceptorBuilder) {
|
||||
requestInterceptorBuilder
|
||||
.withAccessTokenProviderInterceptors(new BasicAuthenticationInterceptor("username", "password"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2013-2021 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 javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
|
||||
import org.springframework.cloud.client.loadbalancer.RetryLoadBalancerInterceptor;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.cloud.openfeign.FeignContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
|
||||
/**
|
||||
* @author Wojciech Mąka
|
||||
*/
|
||||
@SpringBootTest(classes = AccessTokenProviderWithLoadBalancerInterceptorTests.Application.class,
|
||||
webEnvironment = RANDOM_PORT,
|
||||
value = { "security.oauth2.client.id=test-service", "security.oauth2.client.client-id=test-service",
|
||||
"security.oauth2.client.client-secret=test-service",
|
||||
"security.oauth2.client.grant-type=client_credentials", "feign.oauth2.enabled=true",
|
||||
"feign.oauth2.load-balanced=true" })
|
||||
@DirtiesContext
|
||||
public class AccessTokenProviderWithLoadBalancerInterceptorTests {
|
||||
|
||||
@Autowired
|
||||
FeignContext context;
|
||||
|
||||
@Autowired
|
||||
private ConfigurableApplicationContext applicationContext;
|
||||
|
||||
@Test
|
||||
void testOAuth2RequestInterceptorIsLoadBalanced() {
|
||||
AssertableApplicationContext assertableContext = AssertableApplicationContext.get(() -> applicationContext);
|
||||
assertThat(assertableContext).hasSingleBean(Application.SampleClient.class);
|
||||
assertThat(assertableContext).hasSingleBean(OAuth2FeignRequestInterceptor.class);
|
||||
assertThat(assertableContext).getBean(OAuth2FeignRequestInterceptor.class).extracting("accessTokenProvider")
|
||||
.extracting("interceptors").asList()
|
||||
.filteredOn(obj -> RetryLoadBalancerInterceptor.class.equals(obj.getClass())).hasSize(1);
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@EnableFeignClients(
|
||||
clients = { AccessTokenProviderWithLoadBalancerInterceptorTests.Application.SampleClient.class })
|
||||
protected static class Application {
|
||||
|
||||
@GetMapping("/foo")
|
||||
public String foo(HttpServletRequest request) throws IllegalAccessException {
|
||||
if ("Foo".equals(request.getHeader("Foo")) && "Bar".equals(request.getHeader("Bar"))) {
|
||||
return "OK";
|
||||
}
|
||||
else {
|
||||
throw new IllegalAccessException("It should has Foo and Bar header");
|
||||
}
|
||||
}
|
||||
|
||||
@FeignClient(name = "sampleClient")
|
||||
protected interface SampleClient {
|
||||
|
||||
@GetMapping("/foo")
|
||||
String foo();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2013-2021 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 javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
|
||||
import org.springframework.cloud.client.loadbalancer.RetryLoadBalancerInterceptor;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.cloud.openfeign.FeignContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
|
||||
/**
|
||||
* @author Wojciech Mąka
|
||||
*/
|
||||
@SpringBootTest(classes = AccessTokenProviderWithoutLoadBalancerInterceptorTests.Application.class,
|
||||
webEnvironment = RANDOM_PORT,
|
||||
value = { "security.oauth2.client.id=test-service", "security.oauth2.client.client-id=test-service",
|
||||
"security.oauth2.client.client-secret=test-service",
|
||||
"security.oauth2.client.grant-type=client_credentials", "feign.oauth2.enabled=true" })
|
||||
@DirtiesContext
|
||||
public class AccessTokenProviderWithoutLoadBalancerInterceptorTests {
|
||||
|
||||
@Autowired
|
||||
FeignContext context;
|
||||
|
||||
@Autowired
|
||||
private ConfigurableApplicationContext applicationContext;
|
||||
|
||||
@Test
|
||||
void testOAuth2RequestInterceptorIsNotLoadBalanced() {
|
||||
AssertableApplicationContext assertableContext = AssertableApplicationContext.get(() -> applicationContext);
|
||||
assertThat(assertableContext)
|
||||
.hasSingleBean(AccessTokenProviderWithoutLoadBalancerInterceptorTests.Application.SampleClient.class);
|
||||
assertThat(assertableContext).hasSingleBean(OAuth2FeignRequestInterceptor.class);
|
||||
assertThat(assertableContext).getBean(OAuth2FeignRequestInterceptor.class).extracting("accessTokenProvider")
|
||||
.extracting("interceptors").asList()
|
||||
.filteredOn(obj -> RetryLoadBalancerInterceptor.class.equals(obj.getClass())).isEmpty();
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@EnableFeignClients(
|
||||
clients = { AccessTokenProviderWithoutLoadBalancerInterceptorTests.Application.SampleClient.class })
|
||||
protected static class Application {
|
||||
|
||||
@GetMapping("/foo")
|
||||
public String foo(HttpServletRequest request) throws IllegalAccessException {
|
||||
if ("Foo".equals(request.getHeader("Foo")) && "Bar".equals(request.getHeader("Bar"))) {
|
||||
return "OK";
|
||||
}
|
||||
else {
|
||||
throw new IllegalAccessException("It should has Foo and Bar header");
|
||||
}
|
||||
}
|
||||
|
||||
@FeignClient(name = "sampleClient")
|
||||
protected interface SampleClient {
|
||||
|
||||
@GetMapping("/foo")
|
||||
String foo();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,7 +29,7 @@ import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
*
|
||||
* @author João Pedro Evangelista
|
||||
*/
|
||||
final class MockOAuth2ClientContext implements OAuth2ClientContext {
|
||||
public final class MockOAuth2ClientContext implements OAuth2ClientContext {
|
||||
|
||||
private final String value;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user