From 7ee687c79866f59b678c07f083b411e0a9b0d134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Wed, 1 Jun 2016 19:58:46 +1000 Subject: [PATCH 1/2] Support X-Forwarded-Prefix in ForwardedHeaderFilter See SPR-14270 --- .../web/filter/ForwardedHeaderFilter.java | 25 ++++++++++++- .../filter/ForwardedHeaderFilterTests.java | 37 +++++++++++++++++++ 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/filter/ForwardedHeaderFilter.java b/spring-web/src/main/java/org/springframework/web/filter/ForwardedHeaderFilter.java index c21a3ac0c6..c16fb46f9b 100644 --- a/spring-web/src/main/java/org/springframework/web/filter/ForwardedHeaderFilter.java +++ b/spring-web/src/main/java/org/springframework/web/filter/ForwardedHeaderFilter.java @@ -34,6 +34,7 @@ import org.springframework.http.HttpRequest; import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; import org.springframework.web.util.UrlPathHelper; @@ -48,6 +49,7 @@ import org.springframework.web.util.UrlPathHelper; * reflects the client-originated protocol and address. * * @author Rossen Stoyanchev + * @author Eddú Meléndez * @since 4.3 */ public class ForwardedHeaderFilter extends OncePerRequestFilter { @@ -143,13 +145,32 @@ public class ForwardedHeaderFilter extends OncePerRequestFilter { this.host = uriComponents.getHost(); this.port = (port == -1 ? (this.secure ? 443 : 80) : port); - this.contextPath = (pathHelper != null ? pathHelper.getContextPath(request) : request.getContextPath()); - this.requestUri = (pathHelper != null ? pathHelper.getRequestUri(request) : request.getRequestURI()); + this.contextPath = contextPath(request, pathHelper); + this.requestUri = requestUri(request, pathHelper); this.requestUrl = initRequestUrl(this.scheme, this.host, port, this.requestUri); this.headers = initHeaders(request); } + private static String contextPath(HttpServletRequest request, ContextPathHelper pathHelper) { + String contextPath = (pathHelper != null ? pathHelper.getContextPath(request) : request.getContextPath()); + return prependForwardedPrefix(request, contextPath); + } + + private static String requestUri(HttpServletRequest request, ContextPathHelper pathHelper) { + String requestUri = (pathHelper != null ? pathHelper.getRequestUri(request) : request.getRequestURI()); + return prependForwardedPrefix(request, requestUri); + } + + private static String prependForwardedPrefix(HttpServletRequest request, String value) { + String header = request.getHeader("X-Forwarded-Prefix"); + if (StringUtils.hasText(header)) { + value = header + value; + } + UriComponents uriComponents = UriComponentsBuilder.fromUriString(value).build(); + return uriComponents.toUriString(); + } + private static StringBuffer initRequestUrl(String scheme, String host, int port, String path) { StringBuffer sb = new StringBuffer(); sb.append(scheme).append("://").append(host); diff --git a/spring-web/src/test/java/org/springframework/web/filter/ForwardedHeaderFilterTests.java b/spring-web/src/test/java/org/springframework/web/filter/ForwardedHeaderFilterTests.java index bd7a375bd6..c6d54b22bd 100644 --- a/spring-web/src/test/java/org/springframework/web/filter/ForwardedHeaderFilterTests.java +++ b/spring-web/src/test/java/org/springframework/web/filter/ForwardedHeaderFilterTests.java @@ -170,6 +170,43 @@ public class ForwardedHeaderFilterTests { assertEquals("bar", actual.getHeader("foo")); } + @Test + public void requestUriWithForwardedPrefix() throws Exception { + this.request.addHeader("X-Forwarded-Prefix", "/prefix"); + this.request.setRequestURI("/mvc-showcase"); + + HttpServletRequest actual = filterAndGetWrappedRequest(); + assertEquals("http://localhost/prefix/mvc-showcase", actual.getRequestURL() + .toString()); + } + + @Test + public void requestUriWithForwardedPrefixTrailingSlash() throws Exception { + this.request.addHeader("X-Forwarded-Prefix", "/prefix/"); + this.request.setRequestURI("/mvc-showcase"); + + HttpServletRequest actual = filterAndGetWrappedRequest(); + assertEquals("http://localhost/prefix/mvc-showcase", actual.getRequestURL() + .toString()); + } + + @Test + public void contextPathWithForwardedPrefix() throws Exception { + this.request.addHeader("X-Forwarded-Prefix", "/prefix"); + this.request.setContextPath("/mvc-showcase"); + + String actual = filterAndGetContextPath(); + assertEquals("/prefix/mvc-showcase", actual); + } + + @Test + public void contextPathWithForwardedPrefixTrailingSlash() throws Exception { + this.request.addHeader("X-Forwarded-Prefix", "/prefix/"); + this.request.setContextPath("/mvc-showcase"); + + String actual = filterAndGetContextPath(); + assertEquals("/prefix/mvc-showcase", actual); + } private String filterAndGetContextPath() throws ServletException, IOException { return filterAndGetWrappedRequest().getContextPath(); From 1f3ac340fab901be988906678b33c2d97aa4483d Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 1 Jun 2016 15:33:19 -0400 Subject: [PATCH 2/2] Polish X-Forwarded-Prefix handling Issue: SPR-14270 --- .../web/filter/ForwardedHeaderFilter.java | 37 +++++++++++-------- .../filter/ForwardedHeaderFilterTests.java | 7 ++-- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/filter/ForwardedHeaderFilter.java b/spring-web/src/main/java/org/springframework/web/filter/ForwardedHeaderFilter.java index c16fb46f9b..ba2ad448c6 100644 --- a/spring-web/src/main/java/org/springframework/web/filter/ForwardedHeaderFilter.java +++ b/spring-web/src/main/java/org/springframework/web/filter/ForwardedHeaderFilter.java @@ -39,13 +39,14 @@ import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; import org.springframework.web.util.UrlPathHelper; + /** * Filter that wraps the request in order to override its * {@link HttpServletRequest#getServerName() getServerName()}, * {@link HttpServletRequest#getServerPort() getServerPort()}, * {@link HttpServletRequest#getScheme() getScheme()}, and * {@link HttpServletRequest#isSecure() isSecure()} methods with values derived - * from "Fowarded" or "X-Forwarded-*" headers. In effect the wrapped request + * from "Forwarded" or "X-Forwarded-*" headers. In effect the wrapped request * reflects the client-originated protocol and address. * * @author Rossen Stoyanchev @@ -57,11 +58,12 @@ public class ForwardedHeaderFilter extends OncePerRequestFilter { private static final Set FORWARDED_HEADER_NAMES; static { - FORWARDED_HEADER_NAMES = new HashSet(4); + FORWARDED_HEADER_NAMES = new HashSet(5); 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"); } @@ -133,6 +135,7 @@ public class ForwardedHeaderFilter extends OncePerRequestFilter { private final Map> headers; + public ForwardedHeaderRequestWrapper(HttpServletRequest request, ContextPathHelper pathHelper) { super(request); @@ -145,32 +148,23 @@ public class ForwardedHeaderFilter extends OncePerRequestFilter { this.host = uriComponents.getHost(); this.port = (port == -1 ? (this.secure ? 443 : 80) : port); - this.contextPath = contextPath(request, pathHelper); - this.requestUri = requestUri(request, pathHelper); + this.contextPath = initContextPath(request, pathHelper); + this.requestUri = initRequestUri(request, pathHelper); this.requestUrl = initRequestUrl(this.scheme, this.host, port, this.requestUri); - this.headers = initHeaders(request); } - private static String contextPath(HttpServletRequest request, ContextPathHelper pathHelper) { + + private static String initContextPath(HttpServletRequest request, ContextPathHelper pathHelper) { String contextPath = (pathHelper != null ? pathHelper.getContextPath(request) : request.getContextPath()); return prependForwardedPrefix(request, contextPath); } - private static String requestUri(HttpServletRequest request, ContextPathHelper pathHelper) { + private static String initRequestUri(HttpServletRequest request, ContextPathHelper pathHelper) { String requestUri = (pathHelper != null ? pathHelper.getRequestUri(request) : request.getRequestURI()); return prependForwardedPrefix(request, requestUri); } - private static String prependForwardedPrefix(HttpServletRequest request, String value) { - String header = request.getHeader("X-Forwarded-Prefix"); - if (StringUtils.hasText(header)) { - value = header + value; - } - UriComponents uriComponents = UriComponentsBuilder.fromUriString(value).build(); - return uriComponents.toUriString(); - } - private static StringBuffer initRequestUrl(String scheme, String host, int port, String path) { StringBuffer sb = new StringBuffer(); sb.append(scheme).append("://").append(host); @@ -195,6 +189,17 @@ public class ForwardedHeaderFilter extends OncePerRequestFilter { return headers; } + private static String prependForwardedPrefix(HttpServletRequest request, String value) { + String header = request.getHeader("X-Forwarded-Prefix"); + if (StringUtils.hasText(header)) { + while (header.endsWith("/")) { + header = header.substring(0, header.length() - 1); + } + value = header + value; + } + return value; + } + @Override public String getScheme() { diff --git a/spring-web/src/test/java/org/springframework/web/filter/ForwardedHeaderFilterTests.java b/spring-web/src/test/java/org/springframework/web/filter/ForwardedHeaderFilterTests.java index c6d54b22bd..4c9c45256f 100644 --- a/spring-web/src/test/java/org/springframework/web/filter/ForwardedHeaderFilterTests.java +++ b/spring-web/src/test/java/org/springframework/web/filter/ForwardedHeaderFilterTests.java @@ -35,6 +35,7 @@ import static org.junit.Assert.assertTrue; /** * Unit tests for {@link ForwardedHeaderFilter}. * @author Rossen Stoyanchev + * @author Eddú Meléndez */ public class ForwardedHeaderFilterTests { @@ -176,8 +177,7 @@ public class ForwardedHeaderFilterTests { this.request.setRequestURI("/mvc-showcase"); HttpServletRequest actual = filterAndGetWrappedRequest(); - assertEquals("http://localhost/prefix/mvc-showcase", actual.getRequestURL() - .toString()); + assertEquals("http://localhost/prefix/mvc-showcase", actual.getRequestURL().toString()); } @Test @@ -186,8 +186,7 @@ public class ForwardedHeaderFilterTests { this.request.setRequestURI("/mvc-showcase"); HttpServletRequest actual = filterAndGetWrappedRequest(); - assertEquals("http://localhost/prefix/mvc-showcase", actual.getRequestURL() - .toString()); + assertEquals("http://localhost/prefix/mvc-showcase", actual.getRequestURL().toString()); } @Test