Auto forward upstream headers in mvc and webflux proxy exchange.
fixes gh-1183 fixes gh-1193
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user