diff --git a/pom.xml b/pom.xml
index c69a9cc9..24720503 100644
--- a/pom.xml
+++ b/pom.xml
@@ -70,7 +70,7 @@
org.springframework.cloud
spring-cloud-netflix-dependencies
- ${project.version}
+ ${spring-cloud-netflix.version}
pom
import
@@ -81,6 +81,13 @@
pom
import
+
+ org.springframework.cloud
+ spring-cloud-openfeign-dependencies
+ ${project.version}
+ pom
+ import
+
org.springframework.cloud
spring-cloud-test-support
diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml
index 13373977..9a910f62 100644
--- a/spring-cloud-openfeign-core/pom.xml
+++ b/spring-cloud-openfeign-core/pom.xml
@@ -183,6 +183,11 @@
ribbon
test
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-hystrix
+ test
+
diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/HystrixSecurityApplication.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/HystrixSecurityApplication.java
new file mode 100644
index 00000000..58ddb69e
--- /dev/null
+++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/HystrixSecurityApplication.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2013-2016 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
+ *
+ * http://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.hystrix.security;
+
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.cloud.openfeign.hystrix.security.app.UsernameClient;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author Daniel Lavoie
+ */
+@Configuration
+@SpringBootApplication
+@EnableFeignClients(clients = UsernameClient.class)
+public class HystrixSecurityApplication {
+
+}
diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/HystrixSecurityTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/HystrixSecurityTests.java
new file mode 100644
index 00000000..5276b2b5
--- /dev/null
+++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/HystrixSecurityTests.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2013-2016 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
+ *
+ * http://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.hystrix.security;
+
+import java.util.Base64;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.boot.web.server.LocalServerPort;
+import org.springframework.cloud.netflix.hystrix.security.SecurityContextConcurrencyStrategy;
+import org.springframework.cloud.openfeign.hystrix.security.app.CustomConcurrenyStrategy;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.web.client.RestTemplate;
+import com.netflix.hystrix.strategy.HystrixPlugins;
+import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests that a secured web service returning values using a feign client properly access
+ * the security context from a hystrix command.
+ * @author Daniel Lavoie
+ */
+@RunWith(SpringRunner.class)
+@DirtiesContext
+@SpringBootTest(classes = HystrixSecurityApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT,
+ properties = { "username.ribbon.listOfServers=localhost:${local.server.port}",
+ "feign.hystrix.enabled=true"})
+@ActiveProfiles("proxysecurity")
+public class HystrixSecurityTests {
+ @Autowired
+ private CustomConcurrenyStrategy customConcurrenyStrategy;
+
+ @LocalServerPort
+ private String serverPort;
+
+ //TODOO: move to constants in TestAutoConfiguration
+ private String username = "user";
+
+ private String password = "password";
+
+ @Test
+ public void testSecurityConcurrencyStrategyInstalled() {
+ HystrixConcurrencyStrategy concurrencyStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy();
+ assertThat(concurrencyStrategy).isInstanceOf(SecurityContextConcurrencyStrategy.class);
+ }
+
+ @Test
+ public void testFeignHystrixSecurity() {
+ HttpHeaders headers = HystrixSecurityTests.createBasicAuthHeader(username,
+ password);
+
+ String usernameResult = new RestTemplate()
+ .exchange("http://localhost:" + serverPort + "/proxy-username",
+ HttpMethod.GET, new HttpEntity(headers), String.class)
+ .getBody();
+
+ Assert.assertTrue("Username should have been intercepted by feign interceptor.",
+ username.equals(usernameResult));
+
+ Assert.assertTrue("Custom hook should have been called.",
+ customConcurrenyStrategy.isHookCalled());
+ }
+
+ public static HttpHeaders createBasicAuthHeader(final String username,
+ final String password) {
+ return new HttpHeaders() {
+ private static final long serialVersionUID = 1766341693637204893L;
+
+ {
+ String auth = username + ":" + password;
+ byte[] encodedAuth = Base64.getEncoder().encode(auth.getBytes());
+ String authHeader = "Basic " + new String(encodedAuth);
+ this.set("Authorization", authHeader);
+ }
+ };
+ }
+}
diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/CustomConcurrenyStrategy.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/CustomConcurrenyStrategy.java
new file mode 100644
index 00000000..b32657c4
--- /dev/null
+++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/CustomConcurrenyStrategy.java
@@ -0,0 +1,21 @@
+package org.springframework.cloud.openfeign.hystrix.security.app;
+
+import java.util.concurrent.Callable;
+import org.springframework.stereotype.Component;
+import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
+
+@Component
+public class CustomConcurrenyStrategy extends HystrixConcurrencyStrategy {
+ private boolean hookCalled;
+
+ @Override
+ public Callable wrapCallable(Callable callable) {
+ this.hookCalled = true;
+
+ return super.wrapCallable(callable);
+ }
+
+ public boolean isHookCalled() {
+ return hookCalled;
+ }
+}
diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/ProxyUsernameController.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/ProxyUsernameController.java
new file mode 100644
index 00000000..bf4ccd5b
--- /dev/null
+++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/ProxyUsernameController.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2013-2016 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
+ *
+ * http://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.hystrix.security.app;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author Daniel Lavoie
+ */
+@RestController
+@RequestMapping("/proxy-username")
+public class ProxyUsernameController {
+ @Autowired
+ private UsernameClient usernameClient;
+
+ @RequestMapping
+ public String getUsername() {
+ return usernameClient.getUsername();
+ }
+}
diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/TestInterceptor.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/TestInterceptor.java
new file mode 100644
index 00000000..c41a8cbb
--- /dev/null
+++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/TestInterceptor.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2013-2016 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
+ *
+ * http://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.hystrix.security.app;
+
+import feign.RequestInterceptor;
+import feign.RequestTemplate;
+
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Component;
+
+/**
+ * This interceptor should be called from an Hyxtrix command execution thread. It is
+ * access the SecurityContext and settings an http header from the authentication details.
+ *
+ * @author Daniel Lavoie
+ */
+@Component
+public class TestInterceptor implements RequestInterceptor {
+
+ @Override
+ public void apply(RequestTemplate template) {
+ if (SecurityContextHolder.getContext().getAuthentication() != null)
+ template.header("username",
+ SecurityContextHolder.getContext().getAuthentication().getName());
+ }
+}
diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/UsernameClient.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/UsernameClient.java
new file mode 100644
index 00000000..1d245b7c
--- /dev/null
+++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/UsernameClient.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2013-2016 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
+ *
+ * http://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.hystrix.security.app;
+
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+/**
+ * @author Daniel Lavoie
+ */
+@FeignClient("username")
+public interface UsernameClient {
+
+ @RequestMapping("/username")
+ public String getUsername();
+}
diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/UsernameController.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/UsernameController.java
new file mode 100644
index 00000000..daacc0d5
--- /dev/null
+++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/UsernameController.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2013-2016 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
+ *
+ * http://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.hystrix.security.app;
+
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author Daniel Lavoie
+ */
+@RestController
+@RequestMapping("/username")
+public class UsernameController {
+ @RequestMapping
+ public String getUsername(@RequestHeader String username){
+ return username;
+ }
+}
diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonHttpClientConfigurationTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonHttpClientConfigurationTests.java
index 4e35b5d5..c3eeb7c2 100644
--- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonHttpClientConfigurationTests.java
+++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonHttpClientConfigurationTests.java
@@ -14,7 +14,7 @@
* limitations under the License.
*
*/
-package org.springframework.cloud.openfeign.ribbon;
+package org.springframework.cloud.netflix.feign.ribbon;
import java.lang.reflect.Field;
import javax.net.ssl.SSLContextSpi;
@@ -30,10 +30,11 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.cloud.openfeign.ribbon.FeignRibbonClientRetryTests;
import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.ReflectionUtils;
-import org.springframework.web.bind.annotation.RestController;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -42,8 +43,10 @@ import static org.junit.Assert.assertNull;
* @author Ryan Baxter
*/
@RunWith(SpringRunner.class)
-@SpringBootTest(classes = FeignRibbonHttpClientConfigurationTests.Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
+@SpringBootTest(classes = FeignRibbonHttpClientConfigurationTests.FeignRibbonHttpClientConfigurationTestsApplication.class,
+ webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = {"debug=true","feign.httpclient.disableSslValidation=true"})
+@DirtiesContext
public class FeignRibbonHttpClientConfigurationTests {
@Autowired
@@ -77,11 +80,10 @@ public class FeignRibbonHttpClientConfigurationTests {
@Configuration
@EnableAutoConfiguration
- @RestController
- public static class Application {
+ static class FeignRibbonHttpClientConfigurationTestsApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(FeignRibbonClientRetryTests.Application.class)
.run(args);
}
}
-}
+}
\ No newline at end of file
diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonOkHttpClientConfigurationTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonOkHttpClientConfigurationTests.java
index e05a3927..eb6956e5 100644
--- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonOkHttpClientConfigurationTests.java
+++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonOkHttpClientConfigurationTests.java
@@ -14,7 +14,7 @@
* limitations under the License.
*
*/
-package org.springframework.cloud.openfeign.ribbon;
+package org.springframework.cloud.netflix.feign.ribbon;
import okhttp3.OkHttpClient;
@@ -28,18 +28,21 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
+import org.springframework.cloud.openfeign.ribbon.FeignRibbonClientRetryTests;
import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.ReflectionUtils;
-import org.springframework.web.bind.annotation.RestController;
/**
* @author Ryan Baxter
*/
@RunWith(SpringRunner.class)
-@SpringBootTest(classes = FeignRibbonOkHttpClientConfigurationTests.Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
+@SpringBootTest(classes = FeignRibbonOkHttpClientConfigurationTests.FeignRibbonOkHttpClientConfigurationTestsApplication.class,
+ webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = {"debug=true","feign.httpclient.disableSslValidation=true",
"feign.okhttp.enabled=true", "feign.httpclient.enabled=false"})
+@DirtiesContext
public class FeignRibbonOkHttpClientConfigurationTests {
@Autowired
@@ -60,11 +63,10 @@ public class FeignRibbonOkHttpClientConfigurationTests {
@Configuration
@EnableAutoConfiguration
- @RestController
- public static class Application {
+ static class FeignRibbonOkHttpClientConfigurationTestsApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(FeignRibbonClientRetryTests.Application.class)
.run(args);
}
}
-}
+}
\ No newline at end of file
diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml
index aad79132..b8c18265 100644
--- a/spring-cloud-openfeign-dependencies/pom.xml
+++ b/spring-cloud-openfeign-dependencies/pom.xml
@@ -23,6 +23,11 @@
spring-cloud-openfeign-core
${project.version}
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+ ${project.version}
+
io.github.openfeign
feign-core
diff --git a/spring-cloud-starter-openfeign/pom.xml b/spring-cloud-starter-openfeign/pom.xml
index 607b9091..a9262118 100644
--- a/spring-cloud-starter-openfeign/pom.xml
+++ b/spring-cloud-starter-openfeign/pom.xml
@@ -5,6 +5,7 @@
org.springframework.cloud
spring-cloud-openfeign
2.0.0.BUILD-SNAPSHOT
+ ..
spring-cloud-starter-openfeign
Spring Cloud Starter OpenFeign
@@ -15,7 +16,7 @@
https://www.spring.io
- ${basedir}/../../..
+ ${basedir}/../..
@@ -25,7 +26,6 @@
org.springframework.cloud
spring-cloud-openfeign-core
- 2.0.0.BUILD-SNAPSHOT
org.springframework
diff --git a/spring-cloud-starter-openfeign/src/main/resources/META-INF/spring.provides b/spring-cloud-starter-openfeign/src/main/resources/META-INF/spring.provides
index 3dc45a03..ee5b1c80 100644
--- a/spring-cloud-starter-openfeign/src/main/resources/META-INF/spring.provides
+++ b/spring-cloud-starter-openfeign/src/main/resources/META-INF/spring.provides
@@ -1 +1 @@
-provides: spring-platform-netflix-core, eureka-client
\ No newline at end of file
+provides: spring-cloud-openfeign-core
\ No newline at end of file