diff --git a/spring-test/src/main/java/org/springframework/mock/web/reactive/function/server/MockServerRequest.java b/spring-test/src/main/java/org/springframework/mock/web/reactive/function/server/MockServerRequest.java index 80ccb56d9d..9c552f9660 100644 --- a/spring-test/src/main/java/org/springframework/mock/web/reactive/function/server/MockServerRequest.java +++ b/spring-test/src/main/java/org/springframework/mock/web/reactive/function/server/MockServerRequest.java @@ -38,7 +38,6 @@ import org.springframework.http.HttpCookie; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpRange; -import org.springframework.http.HttpRequest; import org.springframework.http.MediaType; import org.springframework.http.codec.HttpMessageReader; import org.springframework.http.codec.multipart.Part; @@ -141,7 +140,7 @@ public class MockServerRequest implements ServerRequest { @Override public UriBuilder uriBuilder() { - return UriComponentsBuilder.fromHttpRequest(new ServerRequestAdapter()); + return UriComponentsBuilder.fromUri(this.uri); } @Override @@ -571,22 +570,4 @@ public class MockServerRequest implements ServerRequest { } - private final class ServerRequestAdapter implements HttpRequest { - - @Override - public String getMethodValue() { - return methodName(); - } - - @Override - public URI getURI() { - return MockServerRequest.this.uri; - } - - @Override - public HttpHeaders getHeaders() { - return MockServerRequest.this.headers.headers; - } - } - } diff --git a/spring-web/src/main/java/org/springframework/web/cors/reactive/CorsUtils.java b/spring-web/src/main/java/org/springframework/web/cors/reactive/CorsUtils.java index 23380447ed..da8569d78c 100644 --- a/spring-web/src/main/java/org/springframework/web/cors/reactive/CorsUtils.java +++ b/spring-web/src/main/java/org/springframework/web/cors/reactive/CorsUtils.java @@ -16,6 +16,8 @@ package org.springframework.web.cors.reactive; +import java.net.URI; + import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.server.reactive.ServerHttpRequest; @@ -49,18 +51,16 @@ public abstract class CorsUtils { } /** - * Check if the request is a same-origin one, based on {@code Origin}, {@code Host}, - * {@code Forwarded}, {@code X-Forwarded-Proto}, {@code X-Forwarded-Host} and - * @code X-Forwarded-Port} headers. + * Check if the request is a same-origin one, based on {@code Origin}, and + * {@code Host} headers. + * + *

Note: as of 5.1 this method ignores + * {@code "Forwarded"} and {@code "X-Forwarded-*"} headers that specify the + * client-originated address. Consider using the {@code ForwardedHeaderFilter} + * to extract and use, or to discard such headers. + * * @return {@code true} if the request is a same-origin one, {@code false} in case * of a cross-origin request - *

Note: this method uses values from "Forwarded" - * (RFC 7239), - * "X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" headers, - * if present, in order to reflect the client-originated address. - * Consider using the {@code ForwardedHeaderFilter} in order to choose from a - * central place whether to extract and use, or to discard such headers. - * See the Spring Framework reference for more on this filter. */ public static boolean isSameOrigin(ServerHttpRequest request) { String origin = request.getHeaders().getOrigin(); @@ -68,9 +68,9 @@ public abstract class CorsUtils { return true; } - UriComponents actualUrl = UriComponentsBuilder.fromHttpRequest(request).build(); - String actualHost = actualUrl.getHost(); - int actualPort = getPort(actualUrl.getScheme(), actualUrl.getPort()); + URI uri = request.getURI(); + String actualHost = uri.getHost(); + int actualPort = getPort(uri.getScheme(), uri.getPort()); Assert.notNull(actualHost, "Actual request host must not be null"); Assert.isTrue(actualPort != -1, "Actual request port must not be undefined"); diff --git a/spring-web/src/main/java/org/springframework/web/util/WebUtils.java b/spring-web/src/main/java/org/springframework/web/util/WebUtils.java index 6af42f4546..7dd736a7cb 100644 --- a/spring-web/src/main/java/org/springframework/web/util/WebUtils.java +++ b/spring-web/src/main/java/org/springframework/web/util/WebUtils.java @@ -18,11 +18,10 @@ package org.springframework.web.util; import java.io.File; import java.io.FileNotFoundException; +import java.net.URI; import java.util.Collection; import java.util.Enumeration; -import java.util.LinkedHashSet; import java.util.Map; -import java.util.Set; import java.util.StringTokenizer; import java.util.TreeMap; import javax.servlet.ServletContext; @@ -138,16 +137,6 @@ public abstract class WebUtils { /** Key for the mutex session attribute */ public static final String SESSION_MUTEX_ATTRIBUTE = WebUtils.class.getName() + ".MUTEX"; - private static final Set FORWARDED_HEADER_NAMES = new LinkedHashSet<>(5); - - static { - FORWARDED_HEADER_NAMES.add("Forwarded"); - FORWARDED_HEADER_NAMES.add("X-Forwarded-Host"); - FORWARDED_HEADER_NAMES.add("X-Forwarded-Port"); - FORWARDED_HEADER_NAMES.add("X-Forwarded-Proto"); - FORWARDED_HEADER_NAMES.add("X-Forwarded-Prefix"); - } - /** * Set a system property to the web application root directory. @@ -677,13 +666,12 @@ public abstract class WebUtils { * Check the given request origin against a list of allowed origins. * A list containing "*" means that all origins are allowed. * An empty list means only same origin is allowed. - *

Note: this method may use values from "Forwarded" - * (RFC 7239), - * "X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" headers, - * if present, in order to reflect the client-originated address. - * Consider using the {@code ForwardedHeaderFilter} in order to choose from a - * central place whether to extract and use, or to discard such headers. - * See the Spring Framework reference for more on this filter. + * + *

Note: as of 5.1 this method ignores + * {@code "Forwarded"} and {@code "X-Forwarded-*"} headers that specify the + * client-originated address. Consider using the {@code ForwardedHeaderFilter} + * to extract and use, or to discard such headers. + * * @return {@code true} if the request origin is valid, {@code false} otherwise * @since 4.1.5 * @see RFC 6454: The Web Origin Concept @@ -708,13 +696,12 @@ public abstract class WebUtils { * Check if the request is a same-origin one, based on {@code Origin}, {@code Host}, * {@code Forwarded}, {@code X-Forwarded-Proto}, {@code X-Forwarded-Host} and * @code X-Forwarded-Port} headers. - *

Note: this method uses values from "Forwarded" - * (RFC 7239), - * "X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" headers, - * if present, in order to reflect the client-originated address. - * Consider using the {@code ForwardedHeaderFilter} in order to choose from a - * central place whether to extract and use, or to discard such headers. - * See the Spring Framework reference for more on this filter. + * + *

Note: as of 5.1 this method ignores + * {@code "Forwarded"} and {@code "X-Forwarded-*"} headers that specify the + * client-originated address. Consider using the {@code ForwardedHeaderFilter} + * to extract and use, or to discard such headers. + * @return {@code true} if the request is a same-origin one, {@code false} in case * of cross-origin request * @since 4.2 @@ -735,21 +722,12 @@ public abstract class WebUtils { scheme = servletRequest.getScheme(); host = servletRequest.getServerName(); port = servletRequest.getServerPort(); - if (containsForwardedHeaders(servletRequest)) { - UriComponents actualUrl = new UriComponentsBuilder() - .scheme(scheme).host(host).port(port) - .adaptFromForwardedHeaders(headers) - .build(); - scheme = actualUrl.getScheme(); - host = actualUrl.getHost(); - port = actualUrl.getPort(); - } } else { - UriComponents actualUrl = UriComponentsBuilder.fromHttpRequest(request).build(); - scheme = actualUrl.getScheme(); - host = actualUrl.getHost(); - port = actualUrl.getPort(); + URI uri = request.getURI(); + scheme = uri.getScheme(); + host = uri.getHost(); + port = uri.getPort(); } UriComponents originUrl = UriComponentsBuilder.fromOriginHeader(origin).build(); @@ -757,15 +735,6 @@ public abstract class WebUtils { getPort(scheme, port) == getPort(originUrl.getScheme(), originUrl.getPort())); } - private static boolean containsForwardedHeaders(HttpServletRequest request) { - for (String headerName : FORWARDED_HEADER_NAMES) { - if (request.getHeader(headerName) != null) { - return true; - } - } - return false; - } - private static int getPort(@Nullable String scheme, int port) { if (port == -1) { if ("http".equals(scheme) || "ws".equals(scheme)) { diff --git a/spring-web/src/test/java/org/springframework/web/cors/reactive/CorsUtilsTests.java b/spring-web/src/test/java/org/springframework/web/cors/reactive/CorsUtilsTests.java index 53dec54593..96ee57a298 100644 --- a/spring-web/src/test/java/org/springframework/web/cors/reactive/CorsUtilsTests.java +++ b/spring-web/src/test/java/org/springframework/web/cors/reactive/CorsUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2018 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. @@ -16,15 +16,19 @@ package org.springframework.web.cors.reactive; +import java.util.concurrent.atomic.AtomicReference; + import org.junit.Test; +import reactor.core.publisher.Mono; import org.springframework.http.HttpHeaders; +import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest; +import org.springframework.mock.web.test.server.MockServerWebExchange; +import org.springframework.web.filter.reactive.ForwardedHeaderFilter; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.springframework.mock.http.server.reactive.test.MockServerHttpRequest.get; -import static org.springframework.mock.http.server.reactive.test.MockServerHttpRequest.options; +import static org.junit.Assert.*; +import static org.springframework.mock.http.server.reactive.test.MockServerHttpRequest.*; /** * Test case for reactive {@link CorsUtils}. @@ -35,19 +39,19 @@ public class CorsUtilsTests { @Test public void isCorsRequest() { - MockServerHttpRequest request = get("/").header(HttpHeaders.ORIGIN, "http://domain.com").build(); + ServerHttpRequest request = get("/").header(HttpHeaders.ORIGIN, "http://domain.com").build(); assertTrue(CorsUtils.isCorsRequest(request)); } @Test public void isNotCorsRequest() { - MockServerHttpRequest request = get("/").build(); + ServerHttpRequest request = get("/").build(); assertFalse(CorsUtils.isCorsRequest(request)); } @Test public void isPreFlightRequest() { - MockServerHttpRequest request = options("/") + ServerHttpRequest request = options("/") .header(HttpHeaders.ORIGIN, "http://domain.com") .header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET") .build(); @@ -56,7 +60,7 @@ public class CorsUtilsTests { @Test public void isNotPreFlightRequest() { - MockServerHttpRequest request = get("/").build(); + ServerHttpRequest request = get("/").build(); assertFalse(CorsUtils.isPreFlightRequest(request)); request = options("/").header(HttpHeaders.ORIGIN, "http://domain.com").build(); @@ -68,31 +72,35 @@ public class CorsUtilsTests { @Test // SPR-16262 public void isSameOriginWithXForwardedHeaders() { - assertTrue(checkSameOriginWithXForwardedHeaders("mydomain1.com", -1, "https", null, -1, "https://mydomain1.com")); - assertTrue(checkSameOriginWithXForwardedHeaders("mydomain1.com", 123, "https", null, -1, "https://mydomain1.com")); - assertTrue(checkSameOriginWithXForwardedHeaders("mydomain1.com", -1, "https", "mydomain2.com", -1, "https://mydomain2.com")); - assertTrue(checkSameOriginWithXForwardedHeaders("mydomain1.com", 123, "https", "mydomain2.com", -1, "https://mydomain2.com")); - assertTrue(checkSameOriginWithXForwardedHeaders("mydomain1.com", -1, "https", "mydomain2.com", 456, "https://mydomain2.com:456")); - assertTrue(checkSameOriginWithXForwardedHeaders("mydomain1.com", 123, "https", "mydomain2.com", 456, "https://mydomain2.com:456")); + String server = "mydomain1.com"; + testWithXForwardedHeaders(server, -1, "https", null, -1, "https://mydomain1.com"); + testWithXForwardedHeaders(server, 123, "https", null, -1, "https://mydomain1.com"); + testWithXForwardedHeaders(server, -1, "https", "mydomain2.com", -1, "https://mydomain2.com"); + testWithXForwardedHeaders(server, 123, "https", "mydomain2.com", -1, "https://mydomain2.com"); + testWithXForwardedHeaders(server, -1, "https", "mydomain2.com", 456, "https://mydomain2.com:456"); + testWithXForwardedHeaders(server, 123, "https", "mydomain2.com", 456, "https://mydomain2.com:456"); } @Test // SPR-16262 public void isSameOriginWithForwardedHeader() { - assertTrue(checkSameOriginWithForwardedHeader("mydomain1.com", -1, "proto=https", "https://mydomain1.com")); - assertTrue(checkSameOriginWithForwardedHeader("mydomain1.com", 123, "proto=https", "https://mydomain1.com")); - assertTrue(checkSameOriginWithForwardedHeader("mydomain1.com", -1, "proto=https; host=mydomain2.com", "https://mydomain2.com")); - assertTrue(checkSameOriginWithForwardedHeader("mydomain1.com", 123, "proto=https; host=mydomain2.com", "https://mydomain2.com")); - assertTrue(checkSameOriginWithForwardedHeader("mydomain1.com", -1, "proto=https; host=mydomain2.com:456", "https://mydomain2.com:456")); - assertTrue(checkSameOriginWithForwardedHeader("mydomain1.com", 123, "proto=https; host=mydomain2.com:456", "https://mydomain2.com:456")); + String server = "mydomain1.com"; + testWithForwardedHeader(server, -1, "proto=https", "https://mydomain1.com"); + testWithForwardedHeader(server, 123, "proto=https", "https://mydomain1.com"); + testWithForwardedHeader(server, -1, "proto=https; host=mydomain2.com", "https://mydomain2.com"); + testWithForwardedHeader(server, 123, "proto=https; host=mydomain2.com", "https://mydomain2.com"); + testWithForwardedHeader(server, -1, "proto=https; host=mydomain2.com:456", "https://mydomain2.com:456"); + testWithForwardedHeader(server, 123, "proto=https; host=mydomain2.com:456", "https://mydomain2.com:456"); } - private boolean checkSameOriginWithXForwardedHeaders(String serverName, int port, String forwardedProto, String forwardedHost, int forwardedPort, String originHeader) { + private void testWithXForwardedHeaders(String serverName, int port, + String forwardedProto, String forwardedHost, int forwardedPort, String originHeader) { + String url = "http://" + serverName; if (port != -1) { url = url + ":" + port; } - MockServerHttpRequest.BaseBuilder builder = get(url) - .header(HttpHeaders.ORIGIN, originHeader); + + MockServerHttpRequest.BaseBuilder builder = get(url).header(HttpHeaders.ORIGIN, originHeader); if (forwardedProto != null) { builder.header("X-Forwarded-Proto", forwardedProto); } @@ -102,18 +110,36 @@ public class CorsUtilsTests { if (forwardedPort != -1) { builder.header("X-Forwarded-Port", String.valueOf(forwardedPort)); } - return CorsUtils.isSameOrigin(builder.build()); + + ServerHttpRequest request = adaptFromForwardedHeaders(builder); + assertTrue(CorsUtils.isSameOrigin(request)); } - private boolean checkSameOriginWithForwardedHeader(String serverName, int port, String forwardedHeader, String originHeader) { + private void testWithForwardedHeader(String serverName, int port, + String forwardedHeader, String originHeader) { + String url = "http://" + serverName; if (port != -1) { url = url + ":" + port; } + MockServerHttpRequest.BaseBuilder builder = get(url) .header("Forwarded", forwardedHeader) .header(HttpHeaders.ORIGIN, originHeader); - return CorsUtils.isSameOrigin(builder.build()); + + ServerHttpRequest request = adaptFromForwardedHeaders(builder); + assertTrue(CorsUtils.isSameOrigin(request)); + } + + // SPR-16668 + private ServerHttpRequest adaptFromForwardedHeaders(MockServerHttpRequest.BaseBuilder builder) { + AtomicReference requestRef = new AtomicReference<>(); + MockServerWebExchange exchange = MockServerWebExchange.from(builder); + new ForwardedHeaderFilter().filter(exchange, exchange2 -> { + requestRef.set(exchange2.getRequest()); + return Mono.empty(); + }).block(); + return requestRef.get(); } } diff --git a/spring-web/src/test/java/org/springframework/web/util/WebUtilsTests.java b/spring-web/src/test/java/org/springframework/web/util/WebUtilsTests.java index ad92f4e953..1e38487841 100644 --- a/spring-web/src/test/java/org/springframework/web/util/WebUtilsTests.java +++ b/spring-web/src/test/java/org/springframework/web/util/WebUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 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. @@ -21,19 +21,20 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.servlet.http.HttpServletRequest; import org.junit.Test; import org.springframework.http.HttpHeaders; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServletServerHttpRequest; +import org.springframework.mock.web.test.MockFilterChain; import org.springframework.mock.web.test.MockHttpServletRequest; +import org.springframework.mock.web.test.MockHttpServletResponse; import org.springframework.util.MultiValueMap; +import org.springframework.web.filter.ForwardedHeaderFilter; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * @author Juergen Hoeller @@ -141,23 +142,25 @@ public class WebUtilsTests { } @Test // SPR-16262 - public void isSameOriginWithXForwardedHeaders() { - assertTrue(checkSameOriginWithXForwardedHeaders("mydomain1.com", -1, "https", null, -1, "https://mydomain1.com")); - assertTrue(checkSameOriginWithXForwardedHeaders("mydomain1.com", 123, "https", null, -1, "https://mydomain1.com")); - assertTrue(checkSameOriginWithXForwardedHeaders("mydomain1.com", -1, "https", "mydomain2.com", -1, "https://mydomain2.com")); - assertTrue(checkSameOriginWithXForwardedHeaders("mydomain1.com", 123, "https", "mydomain2.com", -1, "https://mydomain2.com")); - assertTrue(checkSameOriginWithXForwardedHeaders("mydomain1.com", -1, "https", "mydomain2.com", 456, "https://mydomain2.com:456")); - assertTrue(checkSameOriginWithXForwardedHeaders("mydomain1.com", 123, "https", "mydomain2.com", 456, "https://mydomain2.com:456")); + public void isSameOriginWithXForwardedHeaders() throws Exception { + String server = "mydomain1.com"; + testWithXForwardedHeaders(server, -1, "https", null, -1, "https://mydomain1.com"); + testWithXForwardedHeaders(server, 123, "https", null, -1, "https://mydomain1.com"); + testWithXForwardedHeaders(server, -1, "https", "mydomain2.com", -1, "https://mydomain2.com"); + testWithXForwardedHeaders(server, 123, "https", "mydomain2.com", -1, "https://mydomain2.com"); + testWithXForwardedHeaders(server, -1, "https", "mydomain2.com", 456, "https://mydomain2.com:456"); + testWithXForwardedHeaders(server, 123, "https", "mydomain2.com", 456, "https://mydomain2.com:456"); } @Test // SPR-16262 - public void isSameOriginWithForwardedHeader() { - assertTrue(checkSameOriginWithForwardedHeader("mydomain1.com", -1, "proto=https", "https://mydomain1.com")); - assertTrue(checkSameOriginWithForwardedHeader("mydomain1.com", 123, "proto=https", "https://mydomain1.com")); - assertTrue(checkSameOriginWithForwardedHeader("mydomain1.com", -1, "proto=https; host=mydomain2.com", "https://mydomain2.com")); - assertTrue(checkSameOriginWithForwardedHeader("mydomain1.com", 123, "proto=https; host=mydomain2.com", "https://mydomain2.com")); - assertTrue(checkSameOriginWithForwardedHeader("mydomain1.com", -1, "proto=https; host=mydomain2.com:456", "https://mydomain2.com:456")); - assertTrue(checkSameOriginWithForwardedHeader("mydomain1.com", 123, "proto=https; host=mydomain2.com:456", "https://mydomain2.com:456")); + public void isSameOriginWithForwardedHeader() throws Exception { + String server = "mydomain1.com"; + testWithForwardedHeader(server, -1, "proto=https", "https://mydomain1.com"); + testWithForwardedHeader(server, 123, "proto=https", "https://mydomain1.com"); + testWithForwardedHeader(server, -1, "proto=https; host=mydomain2.com", "https://mydomain2.com"); + testWithForwardedHeader(server, 123, "proto=https; host=mydomain2.com", "https://mydomain2.com"); + testWithForwardedHeader(server, -1, "proto=https; host=mydomain2.com:456", "https://mydomain2.com:456"); + testWithForwardedHeader(server, 123, "proto=https; host=mydomain2.com:456", "https://mydomain2.com:456"); } @@ -183,36 +186,53 @@ public class WebUtilsTests { return WebUtils.isSameOrigin(request); } - private boolean checkSameOriginWithXForwardedHeaders(String serverName, int port, String forwardedProto, String forwardedHost, int forwardedPort, String originHeader) { - MockHttpServletRequest servletRequest = new MockHttpServletRequest(); - ServerHttpRequest request = new ServletServerHttpRequest(servletRequest); - servletRequest.setServerName(serverName); + private void testWithXForwardedHeaders(String serverName, int port, String forwardedProto, + String forwardedHost, int forwardedPort, String originHeader) throws Exception { + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setServerName(serverName); if (port != -1) { - servletRequest.setServerPort(port); + request.setServerPort(port); } if (forwardedProto != null) { - servletRequest.addHeader("X-Forwarded-Proto", forwardedProto); + request.addHeader("X-Forwarded-Proto", forwardedProto); } if (forwardedHost != null) { - servletRequest.addHeader("X-Forwarded-Host", forwardedHost); + request.addHeader("X-Forwarded-Host", forwardedHost); } if (forwardedPort != -1) { - servletRequest.addHeader("X-Forwarded-Port", String.valueOf(forwardedPort)); + request.addHeader("X-Forwarded-Port", String.valueOf(forwardedPort)); } - servletRequest.addHeader(HttpHeaders.ORIGIN, originHeader); - return WebUtils.isSameOrigin(request); + request.addHeader(HttpHeaders.ORIGIN, originHeader); + + HttpServletRequest requestToUse = adaptFromForwardedHeaders(request); + ServerHttpRequest httpRequest = new ServletServerHttpRequest(requestToUse); + + assertTrue(WebUtils.isSameOrigin(httpRequest)); } - private boolean checkSameOriginWithForwardedHeader(String serverName, int port, String forwardedHeader, String originHeader) { - MockHttpServletRequest servletRequest = new MockHttpServletRequest(); - ServerHttpRequest request = new ServletServerHttpRequest(servletRequest); - servletRequest.setServerName(serverName); + private void testWithForwardedHeader(String serverName, int port, String forwardedHeader, + String originHeader) throws Exception { + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setServerName(serverName); if (port != -1) { - servletRequest.setServerPort(port); + request.setServerPort(port); } - servletRequest.addHeader("Forwarded", forwardedHeader); - servletRequest.addHeader(HttpHeaders.ORIGIN, originHeader); - return WebUtils.isSameOrigin(request); + request.addHeader("Forwarded", forwardedHeader); + request.addHeader(HttpHeaders.ORIGIN, originHeader); + + HttpServletRequest requestToUse = adaptFromForwardedHeaders(request); + ServerHttpRequest httpRequest = new ServletServerHttpRequest(requestToUse); + + assertTrue(WebUtils.isSameOrigin(httpRequest)); + } + + // SPR-16668 + private HttpServletRequest adaptFromForwardedHeaders(HttpServletRequest request) throws Exception { + MockFilterChain chain = new MockFilterChain(); + new ForwardedHeaderFilter().doFilter(request, new MockHttpServletResponse(), chain); + return (HttpServletRequest) chain.getRequest(); } } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerRequest.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerRequest.java index 893dc0980c..c6c05cb5a5 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerRequest.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerRequest.java @@ -36,7 +36,6 @@ import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpCookie; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpRange; -import org.springframework.http.HttpRequest; import org.springframework.http.MediaType; import org.springframework.http.codec.HttpMessageReader; import org.springframework.http.codec.multipart.Part; @@ -93,7 +92,7 @@ class DefaultServerRequest implements ServerRequest { @Override public UriBuilder uriBuilder() { - return UriComponentsBuilder.fromHttpRequest(new ServerRequestAdapter()); + return UriComponentsBuilder.fromUri(uri()); } @Override @@ -279,23 +278,4 @@ class DefaultServerRequest implements ServerRequest { } } - - private final class ServerRequestAdapter implements HttpRequest { - - @Override - public String getMethodValue() { - return methodName(); - } - - @Override - public URI getURI() { - return uri(); - } - - @Override - public HttpHeaders getHeaders() { - return request().getHeaders(); - } - } - } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/ServerRequest.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/ServerRequest.java index b01cbf291a..757a8d37ca 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/ServerRequest.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/ServerRequest.java @@ -84,10 +84,13 @@ public interface ServerRequest { /** * Return a {@code UriBuilderComponents} from the URI associated with this - * {@code ServerRequest}, while also overlaying with values from the headers - * "Forwarded" (RFC 7239), - * or "X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" if - * "Forwarded" is not found. + * {@code ServerRequest}. + * + *

Note: as of 5.1 this method ignores + * {@code "Forwarded"} and {@code "X-Forwarded-*"} headers that specify the + * client-originated address. Consider using the {@code ForwardedHeaderFilter} + * to extract and use, or to discard such headers. + * * @return a URI builder */ UriBuilder uriBuilder(); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ServerWebExchangeArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ServerWebExchangeArgumentResolver.java index 7577b9ae35..44ed9865a4 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ServerWebExchangeArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ServerWebExchangeArgumentResolver.java @@ -109,7 +109,7 @@ public class ServerWebExchangeArgumentResolver extends HandlerMethodArgumentReso return timeZone != null ? timeZone.toZoneId() : ZoneId.systemDefault(); } else if (UriBuilder.class == paramType || UriComponentsBuilder.class == paramType) { - return UriComponentsBuilder.fromHttpRequest(exchange.getRequest()); + return UriComponentsBuilder.fromUri(exchange.getRequest().getURI()); } else { // should never happen... diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/MockServerRequest.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/MockServerRequest.java index e48b16deb0..530e826122 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/MockServerRequest.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/MockServerRequest.java @@ -38,7 +38,6 @@ import org.springframework.http.HttpCookie; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpRange; -import org.springframework.http.HttpRequest; import org.springframework.http.MediaType; import org.springframework.http.codec.HttpMessageReader; import org.springframework.http.codec.multipart.Part; @@ -139,7 +138,7 @@ public class MockServerRequest implements ServerRequest { @Override public UriBuilder uriBuilder() { - return UriComponentsBuilder.fromHttpRequest(new ServerRequestAdapter()); + return UriComponentsBuilder.fromUri(this.uri); } @Override @@ -569,23 +568,4 @@ public class MockServerRequest implements ServerRequest { } - private final class ServerRequestAdapter implements HttpRequest { - - @Override - public String getMethodValue() { - return methodName(); - } - - @Override - public URI getURI() { - return MockServerRequest.this.uri; - } - - @Override - public HttpHeaders getHeaders() { - return MockServerRequest.this.headers.headers; - } - } - - } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/ServletUriComponentsBuilder.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/ServletUriComponentsBuilder.java index 237ad3d9e0..04fac7210d 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/ServletUriComponentsBuilder.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/ServletUriComponentsBuilder.java @@ -16,18 +16,14 @@ package org.springframework.web.servlet.support; -import java.util.Enumeration; import javax.servlet.http.HttpServletRequest; -import org.springframework.http.HttpRequest; -import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; -import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; import org.springframework.web.util.UriUtils; import org.springframework.web.util.UrlPathHelper; @@ -81,17 +77,14 @@ public class ServletUriComponentsBuilder extends UriComponentsBuilder { * Prepare a builder from the host, port, scheme, and context path of the * given HttpServletRequest. * - *

Note: This method extracts values from "Forwarded" - * and "X-Forwarded-*" headers if found. See class-level docs. - * - *

As of 4.3.15, this method replaces the contextPath with the value - * of "X-Forwarded-Prefix" rather than prepending, thus aligning with - * {@code ForwardedHeaderFiller}. + *

Note: as of 5.1 this method ignores + * {@code "Forwarded"} and {@code "X-Forwarded-*"} headers that specify the + * client-originated address. Consider using the {@code ForwardedHeaderFilter} + * to extract and use, or to discard such headers. */ public static ServletUriComponentsBuilder fromContextPath(HttpServletRequest request) { ServletUriComponentsBuilder builder = initFromRequest(request); - String forwardedPrefix = getForwardedPrefix(request); - builder.replacePath(forwardedPrefix != null ? forwardedPrefix : request.getContextPath()); + builder.replacePath(request.getContextPath()); return builder; } @@ -103,12 +96,10 @@ public class ServletUriComponentsBuilder extends UriComponentsBuilder { * {@code "/"} or {@code "*.do"}, the result will be the same as * if calling {@link #fromContextPath(HttpServletRequest)}. * - *

Note: This method extracts values from "Forwarded" - * and "X-Forwarded-*" headers if found. See class-level docs. - * - *

As of 4.3.15, this method replaces the contextPath with the value - * of "X-Forwarded-Prefix" rather than prepending, thus aligning with - * {@code ForwardedHeaderFiller}. + *

Note: as of 5.1 this method ignores + * {@code "Forwarded"} and {@code "X-Forwarded-*"} headers that specify the + * client-originated address. Consider using the {@code ForwardedHeaderFilter} + * to extract and use, or to discard such headers. */ public static ServletUriComponentsBuilder fromServletMapping(HttpServletRequest request) { ServletUriComponentsBuilder builder = fromContextPath(request); @@ -122,16 +113,14 @@ public class ServletUriComponentsBuilder extends UriComponentsBuilder { * Prepare a builder from the host, port, scheme, and path (but not the query) * of the HttpServletRequest. * - *

Note: This method extracts values from "Forwarded" - * and "X-Forwarded-*" headers if found. See class-level docs. - * - *

As of 4.3.15, this method replaces the contextPath with the value - * of "X-Forwarded-Prefix" rather than prepending, thus aligning with - * {@code ForwardedHeaderFiller}. + *

Note: as of 5.1 this method ignores + * {@code "Forwarded"} and {@code "X-Forwarded-*"} headers that specify the + * client-originated address. Consider using the {@code ForwardedHeaderFilter} + * to extract and use, or to discard such headers. */ public static ServletUriComponentsBuilder fromRequestUri(HttpServletRequest request) { ServletUriComponentsBuilder builder = initFromRequest(request); - builder.initPath(getRequestUriWithForwardedPrefix(request)); + builder.initPath(request.getRequestURI()); return builder; } @@ -139,16 +128,14 @@ public class ServletUriComponentsBuilder extends UriComponentsBuilder { * Prepare a builder by copying the scheme, host, port, path, and * query string of an HttpServletRequest. * - *

Note: This method extracts values from "Forwarded" - * and "X-Forwarded-*" headers if found. See class-level docs. - * - *

As of 4.3.15, this method replaces the contextPath with the value - * of "X-Forwarded-Prefix" rather than prepending, thus aligning with - * {@code ForwardedHeaderFiller}. + *

Note: as of 5.1 this method ignores + * {@code "Forwarded"} and {@code "X-Forwarded-*"} headers that specify the + * client-originated address. Consider using the {@code ForwardedHeaderFilter} + * to extract and use, or to discard such headers. */ public static ServletUriComponentsBuilder fromRequest(HttpServletRequest request) { ServletUriComponentsBuilder builder = initFromRequest(request); - builder.initPath(getRequestUriWithForwardedPrefix(request)); + builder.initPath(request.getRequestURI()); builder.query(request.getQueryString()); return builder; } @@ -157,11 +144,9 @@ public class ServletUriComponentsBuilder extends UriComponentsBuilder { * Initialize a builder with a scheme, host,and port (but not path and query). */ private static ServletUriComponentsBuilder initFromRequest(HttpServletRequest request) { - HttpRequest httpRequest = new ServletServerHttpRequest(request); - UriComponents uriComponents = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); - String scheme = uriComponents.getScheme(); - String host = uriComponents.getHost(); - int port = uriComponents.getPort(); + String scheme = request.getScheme(); + String host = request.getServerName(); + int port = request.getServerPort(); ServletUriComponentsBuilder builder = new ServletUriComponentsBuilder(); builder.scheme(scheme); @@ -172,37 +157,6 @@ public class ServletUriComponentsBuilder extends UriComponentsBuilder { return builder; } - @Nullable - private static String getForwardedPrefix(HttpServletRequest request) { - String prefix = null; - Enumeration names = request.getHeaderNames(); - while (names.hasMoreElements()) { - String name = names.nextElement(); - if ("X-Forwarded-Prefix".equalsIgnoreCase(name)) { - prefix = request.getHeader(name); - } - } - if (prefix != null) { - while (prefix.endsWith("/")) { - prefix = prefix.substring(0, prefix.length() - 1); - } - } - return prefix; - } - - private static String getRequestUriWithForwardedPrefix(HttpServletRequest request) { - String path = request.getRequestURI(); - String forwardedPrefix = getForwardedPrefix(request); - if (forwardedPrefix != null) { - String contextPath = request.getContextPath(); - if (!StringUtils.isEmpty(contextPath) && !contextPath.equals("/") && path.startsWith(contextPath)) { - path = path.substring(contextPath.length()); - } - path = forwardedPrefix + path; - } - return path; - } - // Alternative methods relying on RequestContextHolder to find the request diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilderTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilderTests.java index 61cbe330bd..205aeca886 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilderTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilderTests.java @@ -23,6 +23,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.Arrays; import java.util.List; +import javax.servlet.http.HttpServletRequest; import org.hamcrest.Matchers; import org.joda.time.DateTime; @@ -36,7 +37,9 @@ import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat.ISO; import org.springframework.http.HttpEntity; import org.springframework.http.MediaType; +import org.springframework.mock.web.test.MockFilterChain; import org.springframework.mock.web.test.MockHttpServletRequest; +import org.springframework.mock.web.test.MockHttpServletResponse; import org.springframework.mock.web.test.MockServletContext; import org.springframework.stereotype.Controller; import org.springframework.util.MultiValueMap; @@ -49,6 +52,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; +import org.springframework.web.filter.ForwardedHeaderFilter; import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.config.annotation.EnableWebMvc; @@ -136,29 +140,40 @@ public class MvcUriComponentsBuilderTests { } @Test - public void usesForwardedHostAsHostIfHeaderIsSet() { + public void usesForwardedHostAsHostIfHeaderIsSet() throws Exception { this.request.addHeader("X-Forwarded-Host", "somethingDifferent"); + adaptRequestFromForwardedHeaders(); UriComponents uriComponents = fromController(PersonControllerImpl.class).build(); assertThat(uriComponents.toUriString(), startsWith("http://somethingDifferent")); } @Test - public void usesForwardedHostAndPortFromHeader() { + public void usesForwardedHostAndPortFromHeader() throws Exception { request.addHeader("X-Forwarded-Host", "foobar:8088"); + adaptRequestFromForwardedHeaders(); UriComponents uriComponents = fromController(PersonControllerImpl.class).build(); assertThat(uriComponents.toUriString(), startsWith("http://foobar:8088")); } @Test - public void usesFirstHostOfXForwardedHost() { - request.addHeader("X-Forwarded-Host", "barfoo:8888, localhost:8088"); + public void usesFirstHostOfXForwardedHost() throws Exception { + this.request.addHeader("X-Forwarded-Host", "barfoo:8888, localhost:8088"); + adaptRequestFromForwardedHeaders(); UriComponents uriComponents = fromController(PersonControllerImpl.class).build(); assertThat(uriComponents.toUriString(), startsWith("http://barfoo:8888")); } + // SPR-16668 + private void adaptRequestFromForwardedHeaders() throws Exception { + MockFilterChain chain = new MockFilterChain(); + new ForwardedHeaderFilter().doFilter(this.request, new MockHttpServletResponse(), chain); + HttpServletRequest adaptedRequest = (HttpServletRequest) chain.getRequest(); + RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(adaptedRequest)); + } + @Test public void fromMethodNamePathVariable() { UriComponents uriComponents = fromMethodName(ControllerWithMethods.class, diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/support/ServletUriComponentsBuilderTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/support/ServletUriComponentsBuilderTests.java index 605c03ff9c..eef2afcd3b 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/support/ServletUriComponentsBuilderTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/support/ServletUriComponentsBuilderTests.java @@ -16,12 +16,17 @@ package org.springframework.web.servlet.support; +import javax.servlet.http.HttpServletRequest; + import org.junit.Before; import org.junit.Test; +import org.springframework.mock.web.test.MockFilterChain; import org.springframework.mock.web.test.MockHttpServletRequest; +import org.springframework.mock.web.test.MockHttpServletResponse; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.filter.ForwardedHeaderFilter; import org.springframework.web.util.UriComponents; import static org.junit.Assert.*; @@ -78,10 +83,10 @@ public class ServletUriComponentsBuilderTests { assertEquals("https://localhost:9043/mvc-showcase", result); } - // Most X-Forwarded-* tests in UriComponentsBuilderTests + // Some X-Forwarded-* tests in addition to the ones in UriComponentsBuilderTests @Test - public void fromRequestWithForwardedHostAndPort() { + public void fromRequestWithForwardedHostAndPort() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); request.setScheme("http"); request.setServerName("localhost"); @@ -90,7 +95,10 @@ public class ServletUriComponentsBuilderTests { request.addHeader("X-Forwarded-Proto", "https"); request.addHeader("X-Forwarded-Host", "84.198.58.199"); request.addHeader("X-Forwarded-Port", "443"); - UriComponents result = ServletUriComponentsBuilder.fromRequest(request).build(); + + HttpServletRequest requestToUse = adaptFromForwardedHeaders(request); + UriComponents result = ServletUriComponentsBuilder.fromRequest(requestToUse).build(); + assertEquals("https://84.198.58.199/mvc-showcase", result.toString()); } @@ -103,29 +111,38 @@ public class ServletUriComponentsBuilderTests { } @Test // SPR-16650 - public void fromRequestWithForwardedPrefix() { + public void fromRequestWithForwardedPrefix() throws Exception { this.request.addHeader("X-Forwarded-Prefix", "/prefix"); this.request.setContextPath("/mvc-showcase"); this.request.setRequestURI("/mvc-showcase/bar"); - UriComponents result = ServletUriComponentsBuilder.fromRequest(this.request).build(); + + HttpServletRequest requestToUse = adaptFromForwardedHeaders(this.request); + UriComponents result = ServletUriComponentsBuilder.fromRequest(requestToUse).build(); + assertEquals("http://localhost/prefix/bar", result.toUriString()); } @Test // SPR-16650 - public void fromRequestWithForwardedPrefixTrailingSlash() { + public void fromRequestWithForwardedPrefixTrailingSlash() throws Exception { this.request.addHeader("X-Forwarded-Prefix", "/foo/"); this.request.setContextPath("/spring-mvc-showcase"); this.request.setRequestURI("/spring-mvc-showcase/bar"); - UriComponents result = ServletUriComponentsBuilder.fromRequest(this.request).build(); + + HttpServletRequest requestToUse = adaptFromForwardedHeaders(this.request); + UriComponents result = ServletUriComponentsBuilder.fromRequest(requestToUse).build(); + assertEquals("http://localhost/foo/bar", result.toUriString()); } @Test // SPR-16650 - public void fromRequestWithForwardedPrefixRoot() { + public void fromRequestWithForwardedPrefixRoot() throws Exception { this.request.addHeader("X-Forwarded-Prefix", "/"); this.request.setContextPath("/mvc-showcase"); this.request.setRequestURI("/mvc-showcase/bar"); - UriComponents result = ServletUriComponentsBuilder.fromRequest(this.request).build(); + + HttpServletRequest requestToUse = adaptFromForwardedHeaders(this.request); + UriComponents result = ServletUriComponentsBuilder.fromRequest(requestToUse).build(); + assertEquals("http://localhost/bar", result.toUriString()); } @@ -138,11 +155,14 @@ public class ServletUriComponentsBuilderTests { } @Test // SPR-16650 - public void fromContextPathWithForwardedPrefix() { + public void fromContextPathWithForwardedPrefix() throws Exception { this.request.addHeader("X-Forwarded-Prefix", "/prefix"); this.request.setContextPath("/mvc-showcase"); this.request.setRequestURI("/mvc-showcase/simple"); - String result = ServletUriComponentsBuilder.fromContextPath(this.request).build().toUriString(); + + HttpServletRequest requestToUse = adaptFromForwardedHeaders(this.request); + String result = ServletUriComponentsBuilder.fromContextPath(requestToUse).build().toUriString(); + assertEquals("http://localhost/prefix", result); } @@ -156,12 +176,15 @@ public class ServletUriComponentsBuilderTests { } @Test // SPR-16650 - public void fromServletMappingWithForwardedPrefix() { + public void fromServletMappingWithForwardedPrefix() throws Exception { this.request.addHeader("X-Forwarded-Prefix", "/prefix"); this.request.setContextPath("/mvc-showcase"); this.request.setServletPath("/app"); this.request.setRequestURI("/mvc-showcase/app/simple"); - String result = ServletUriComponentsBuilder.fromServletMapping(this.request).build().toUriString(); + + HttpServletRequest requestToUse = adaptFromForwardedHeaders(this.request); + String result = ServletUriComponentsBuilder.fromServletMapping(requestToUse).build().toUriString(); + assertEquals("http://localhost/prefix/app", result); } @@ -194,4 +217,12 @@ public class ServletUriComponentsBuilderTests { ServletUriComponentsBuilder builder = ServletUriComponentsBuilder.fromRequestUri(this.request); assertNull(builder.removePathExtension()); } + + // SPR-16668 + private HttpServletRequest adaptFromForwardedHeaders(HttpServletRequest request) throws Exception { + MockFilterChain chain = new MockFilterChain(); + new ForwardedHeaderFilter().doFilter(request, new MockHttpServletResponse(), chain); + return (HttpServletRequest) chain.getRequest(); + } + } diff --git a/src/docs/asciidoc/web/webflux.adoc b/src/docs/asciidoc/web/webflux.adoc index e0046bfc75..3f8cb1bda1 100644 --- a/src/docs/asciidoc/web/webflux.adoc +++ b/src/docs/asciidoc/web/webflux.adoc @@ -1417,10 +1417,8 @@ etc, and is equivalent to `required=false`. See <> for more details. | `UriComponentsBuilder` -| For preparing a URL relative to the current request's host, port, scheme, context path, and - the literal part of the servlet mapping also taking into account `Forwarded` and - `X-Forwarded-*` headers. - // TODO: See <>. +| For preparing a URL relative to the current request's host, port, scheme, and path. + See <>. | `@SessionAttribute` | For access to any session attribute; in contrast to model attributes stored in the session @@ -2499,7 +2497,7 @@ Javadoc for more details. -[[mvc-uri-building]] +[[webflux-uri-building]] == URI Links [.small]#<># diff --git a/src/docs/asciidoc/web/webmvc.adoc b/src/docs/asciidoc/web/webmvc.adoc index 2c74b2947e..66eba8dec6 100644 --- a/src/docs/asciidoc/web/webmvc.adoc +++ b/src/docs/asciidoc/web/webmvc.adoc @@ -1689,8 +1689,7 @@ etc, and is equivalent to `required=false`. | `UriComponentsBuilder` | For preparing a URL relative to the current request's host, port, scheme, context path, and - the literal part of the servlet mapping also taking into account `Forwarded` and - `X-Forwarded-*` headers. See <>. + the literal part of the servlet mapping. See <>. | `@SessionAttribute` | For access to any session attribute; in contrast to model attributes stored in the session @@ -3098,7 +3097,7 @@ Javadoc for more details. [[mvc-uri-building]] == URI Links -[.small]#<># +[.small]#<># This section describes various options available in the Spring Framework to prepare URIs. @@ -3148,14 +3147,12 @@ You can create URIs relative to a Servlet (e.g. `/main/{asterisk}`): .path("/accounts").build() ---- -[CAUTION] +[NOTE] ==== -`ServletUriComponentsBuilder` detects and uses information from the "Forwarded", -"X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" headers, so the resulting -links reflect the original request. You need to ensure that your application is behind -a trusted proxy which filters out such headers coming from outside. Also consider using -the <> which processes such headers once -per request, and also provides an option to remove and ignore such headers. +As of 5.1 `ServletUriComponentsBuilder` ignores information from the "Forwarded", +"X-Forwarded-*" headers, that specify the client-originated address. Consider using the +<> to extract and use, or to discard +such headers. ==== @@ -3243,14 +3240,12 @@ with a base URL and then use the instance-based "withXxx" methods. For example: URI uri = uriComponents.encode().toUri(); ---- -[CAUTION] +[NOTE] ==== -`MvcUriComponentsBuilder` detects and uses information from the "Forwarded", -"X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" headers, so the resulting -links reflect the original request. You need to ensure that your application is behind -a trusted proxy which filters out such headers coming from outside. Also consider using -the <> which processes such headers once -per request, and also provides an option to remove and ignore such headers. +As of 5.1 `MvcUriComponentsBuilder` ignores information from the "Forwarded", +"X-Forwarded-*" headers, that specify the client-originated address. Consider using the +<> to extract and use, or to discard +such headers. ====