Auto forward upstream headers in mvc and webflux proxy exchange.

fixes gh-1183
fixes gh-1193
This commit is contained in:
Tim Ysewyn
2019-07-23 18:00:01 +02:00
committed by Spencer Gibb
parent 138ea51ec2
commit 3f5c0b26ce
8 changed files with 107 additions and 12 deletions

View File

@@ -18,8 +18,12 @@ package org.springframework.cloud.gateway.mvc.config;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.springframework.cloud.gateway.mvc.ProxyExchange;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
@@ -31,7 +35,7 @@ import org.springframework.web.method.support.ModelAndViewContainer;
/**
* @author Dave Syer
*
* @author Tim Ysewyn
*/
public class ProxyExchangeArgumentResolver implements HandlerMethodArgumentResolver {
@@ -39,6 +43,8 @@ public class ProxyExchangeArgumentResolver implements HandlerMethodArgumentResol
private HttpHeaders headers;
private Set<String> autoForwardedHeaders;
private Set<String> sensitive;
public ProxyExchangeArgumentResolver(RestTemplate builder) {
@@ -49,6 +55,10 @@ public class ProxyExchangeArgumentResolver implements HandlerMethodArgumentResol
this.headers = headers;
}
public void setAutoForwardedHeaders(Set<String> autoForwardedHeaders) {
this.autoForwardedHeaders = autoForwardedHeaders;
}
public void setSensitive(Set<String> sensitive) {
this.sensitive = sensitive;
}
@@ -65,6 +75,9 @@ public class ProxyExchangeArgumentResolver implements HandlerMethodArgumentResol
ProxyExchange<?> proxy = new ProxyExchange<>(rest, webRequest, mavContainer,
binderFactory, type(parameter));
proxy.headers(headers);
if (this.autoForwardedHeaders.size() > 0) {
proxy.headers(extractAutoForwardedHeaders(webRequest));
}
if (sensitive != null) {
proxy.sensitive(sensitive.toArray(new String[0]));
}
@@ -80,4 +93,19 @@ public class ProxyExchangeArgumentResolver implements HandlerMethodArgumentResol
return type;
}
private HttpHeaders extractAutoForwardedHeaders(NativeWebRequest webRequest) {
HttpServletRequest nativeRequest = webRequest
.getNativeRequest(HttpServletRequest.class);
Enumeration<String> headerNames = nativeRequest.getHeaderNames();
HttpHeaders headers = new HttpHeaders();
while (headerNames.hasMoreElements()) {
String header = headerNames.nextElement();
if (this.autoForwardedHeaders.contains(header)) {
headers.addAll(header,
Collections.list(nativeRequest.getHeaders(header)));
}
}
return headers;
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.cloud.gateway.mvc.config;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
@@ -29,6 +30,7 @@ import org.springframework.http.HttpHeaders;
* <code>@RequestMapping</code> methods.
*
* @author Dave Syer
* @author Tim Ysewyn
*
*/
@ConfigurationProperties("spring.cloud.gateway.proxy")
@@ -39,6 +41,11 @@ public class ProxyProperties {
*/
private Map<String, String> headers = new LinkedHashMap<>();
/**
* A set of header names that should be send downstream by default.
*/
private Set<String> autoForward = new HashSet<>();
/**
* A set of sensitive header names that will not be sent downstream by default.
*/
@@ -52,6 +59,14 @@ public class ProxyProperties {
this.headers = headers;
}
public Set<String> getAutoForward() {
return autoForward;
}
public void setAutoForward(Set<String> autoForward) {
this.autoForward = autoForward;
}
public Set<String> getSensitive() {
return sensitive;
}

View File

@@ -43,6 +43,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
* <code>@RequestMapping</code> methods.
*
* @author Dave Syer
* @author Tim Ysewyn
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication
@@ -69,6 +70,7 @@ public class ProxyResponseAutoConfiguration implements WebMvcConfigurer {
ProxyExchangeArgumentResolver resolver = new ProxyExchangeArgumentResolver(
template);
resolver.setHeaders(proxy.convertHeaders());
resolver.setAutoForwardedHeaders(proxy.getAutoForward());
resolver.setSensitive(proxy.getSensitive()); // can be null
return resolver;
}

View File

@@ -55,7 +55,8 @@ import org.springframework.web.util.UriComponentsBuilder;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@SpringBootTest(properties = {
"spring.cloud.gateway.proxy.auto-forward=baz" }, webEnvironment = WebEnvironment.RANDOM_PORT)
@ContextConfiguration(classes = TestApplication.class)
public class ProductionConfigurationTests {
@@ -247,15 +248,21 @@ public class ProductionConfigurationTests {
@Test
@SuppressWarnings({ "Duplicates", "unchecked" })
public void headers() throws Exception {
Map<String, List<String>> headers = rest.exchange(RequestEntity
.get(rest.getRestTemplate().getUriTemplateHandler()
.expand("/proxy/headers"))
.header("foo", "bar").header("abc", "xyz").build(), Map.class).getBody();
Map<String, List<String>> headers = rest
.exchange(
RequestEntity
.get(rest.getRestTemplate().getUriTemplateHandler()
.expand("/proxy/headers"))
.header("foo", "bar").header("abc", "xyz")
.header("baz", "fob").build(),
Map.class)
.getBody();
assertThat(headers).doesNotContainKey("foo").doesNotContainKey("hello")
.containsKeys("bar", "abc");
assertThat(headers.get("bar")).containsOnly("hello");
assertThat(headers.get("abc")).containsOnly("123");
assertThat(headers.get("baz")).containsOnly("fob");
}
@Test

View File

@@ -34,7 +34,7 @@ import org.springframework.web.server.ServerWebExchange;
/**
* @author Dave Syer
*
* @author Tim Ysewyn
*/
public class ProxyExchangeArgumentResolver implements HandlerMethodArgumentResolver {
@@ -42,6 +42,8 @@ public class ProxyExchangeArgumentResolver implements HandlerMethodArgumentResol
private HttpHeaders headers;
private Set<String> autoForwardedHeaders;
private Set<String> sensitive;
public ProxyExchangeArgumentResolver(WebClient builder) {
@@ -52,6 +54,10 @@ public class ProxyExchangeArgumentResolver implements HandlerMethodArgumentResol
this.headers = headers;
}
public void setAutoForwardedHeaders(Set<String> autoForwardedHeaders) {
this.autoForwardedHeaders = autoForwardedHeaders;
}
public void setSensitive(Set<String> sensitive) {
this.sensitive = sensitive;
}
@@ -79,10 +85,23 @@ public class ProxyExchangeArgumentResolver implements HandlerMethodArgumentResol
ProxyExchange<?> proxy = new ProxyExchange<>(rest, exchange, bindingContext,
type(parameter));
proxy.headers(headers);
if (this.autoForwardedHeaders.size() > 0) {
proxy.headers(extractAutoForwardedHeaders(exchange));
}
if (sensitive != null) {
proxy.sensitive(sensitive.toArray(new String[0]));
}
return Mono.just(proxy);
}
private HttpHeaders extractAutoForwardedHeaders(ServerWebExchange exchange) {
HttpHeaders headers = new HttpHeaders();
exchange.getRequest().getHeaders().forEach((header, values) -> {
if (this.autoForwardedHeaders.contains(header)) {
headers.addAll(header, values);
}
});
return headers;
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.cloud.gateway.webflux.config;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
@@ -29,6 +30,7 @@ import org.springframework.http.HttpHeaders;
* <code>@RequestMapping</code> methods.
*
* @author Dave Syer
* @author Tim Ysewyn
*
*/
@ConfigurationProperties("spring.cloud.gateway.proxy")
@@ -39,6 +41,11 @@ public class ProxyProperties {
*/
private Map<String, String> headers = new LinkedHashMap<>();
/**
* A set of header names that should be send downstream by default.
*/
private Set<String> autoForward = new HashSet<>();
/**
* A set of sensitive header names that will not be sent downstream by default.
*/
@@ -52,6 +59,14 @@ public class ProxyProperties {
this.headers = headers;
}
public Set<String> getAutoForward() {
return autoForward;
}
public void setAutoForward(Set<String> autoForward) {
this.autoForward = autoForward;
}
public Set<String> getSensitive() {
return sensitive;
}

View File

@@ -37,6 +37,7 @@ import org.springframework.web.reactive.result.method.annotation.ArgumentResolve
* <code>@RequestMapping</code> methods.
*
* @author Dave Syer
* @author Tim Ysewyn
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication
@@ -56,6 +57,7 @@ public class ProxyResponseAutoConfiguration implements WebFluxConfigurer {
ProxyExchangeArgumentResolver resolver = new ProxyExchangeArgumentResolver(
template);
resolver.setHeaders(proxy.convertHeaders());
resolver.setAutoForwardedHeaders(proxy.getAutoForward());
resolver.setSensitive(proxy.getSensitive()); // can be null
return resolver;
}

View File

@@ -58,7 +58,8 @@ import org.springframework.web.util.UriComponentsBuilder;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@SpringBootTest(properties = {
"spring.cloud.gateway.proxy.auto-forward=baz" }, webEnvironment = WebEnvironment.RANDOM_PORT)
@ContextConfiguration(classes = TestApplication.class)
@DirtiesContext
public class ProductionConfigurationTests {
@@ -193,15 +194,21 @@ public class ProductionConfigurationTests {
@Test
@SuppressWarnings({ "Duplicates", "unchecked" })
public void headers() throws Exception {
Map<String, List<String>> headers = rest.exchange(RequestEntity
.get(rest.getRestTemplate().getUriTemplateHandler()
.expand("/proxy/headers"))
.header("foo", "bar").header("abc", "xyz").build(), Map.class).getBody();
Map<String, List<String>> headers = rest
.exchange(
RequestEntity
.get(rest.getRestTemplate().getUriTemplateHandler()
.expand("/proxy/headers"))
.header("foo", "bar").header("abc", "xyz")
.header("baz", "fob").build(),
Map.class)
.getBody();
assertThat(headers).doesNotContainKey("foo").doesNotContainKey("hello")
.containsKeys("bar", "abc");
assertThat(headers.get("bar")).containsOnly("hello");
assertThat(headers.get("abc")).containsOnly("123");
assertThat(headers.get("baz")).containsOnly("fob");
}
@Test