Add Support for ForwardedHeaderFilter sendRedirect

Previously ForwrdedHeaderFilter did not ensure that
HttpServletResponse.sendRedirect worked properly based on
X-Forwarded-* headers.

This commit updates ForwardedHeaderFilter to overrided the
HttpServletResponse.sendRedirect method to ensure
X-Forwarded-* headers are honored.

Issue SPR-15020
This commit is contained in:
Rob Winch
2016-12-14 16:57:18 -06:00
committed by Rossen Stoyanchev
parent e16d753439
commit 5e774e0b44
2 changed files with 217 additions and 6 deletions

View File

@@ -28,26 +28,31 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import org.springframework.http.HttpRequest;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedCaseInsensitiveMap;
import org.springframework.util.StringUtils;
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
* Filter that wraps the request and response 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 "Forwarded" or "X-Forwarded-*" headers. In effect the wrapped request
* reflects the client-originated protocol and address.
* {@link HttpServletRequest#getScheme() getScheme()},
* {@link HttpServletRequest#isSecure() isSecure()},
* {@link HttpServletResponse#sendRedirect(String) sendRedirect(String)},
* methods with values derived from "Forwarded" or "X-Forwarded-*"
* headers. In effect the wrapped request and response reflects the
* client-originated protocol and address.
*
* @author Rossen Stoyanchev
* @author Eddú Meléndez
* @author Rob Winch
* @since 4.3
*/
public class ForwardedHeaderFilter extends OncePerRequestFilter {
@@ -93,7 +98,9 @@ public class ForwardedHeaderFilter extends OncePerRequestFilter {
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
filterChain.doFilter(new ForwardedHeaderRequestWrapper(request, this.pathHelper), response);
ForwardedHeaderRequestWrapper wrappedRequest = new ForwardedHeaderRequestWrapper(request, this.pathHelper);
ForwardedHeaderResponseWrapper wrappedResponse = new ForwardedHeaderResponseWrapper(response, wrappedRequest);
filterChain.doFilter(wrappedRequest, wrappedResponse);
}
@@ -222,4 +229,52 @@ public class ForwardedHeaderFilter extends OncePerRequestFilter {
}
}
private static class ForwardedHeaderResponseWrapper extends HttpServletResponseWrapper {
private static final String FOLDER_SEPARATOR = "/";
private final HttpServletRequest request;
public ForwardedHeaderResponseWrapper(HttpServletResponse response, HttpServletRequest request) {
super(response);
this.request = request;
}
@Override
public void sendRedirect(String location) throws IOException {
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(location);
// Absolute location
if (builder.build().getScheme() != null) {
super.sendRedirect(location);
return;
}
// Network-path reference
if(location.startsWith("//")) {
String scheme = this.request.getScheme();
super.sendRedirect(builder.scheme(scheme).toUriString());
return;
}
// Relative to Servlet container root or to current request
String path;
if (location.startsWith(FOLDER_SEPARATOR)) {
path = this.request.getContextPath() + location;
}
else {
path = StringUtils.applyRelativePath(this.request.getRequestURI(), location);
}
String result = UriComponentsBuilder
.fromHttpRequest(new ServletServerHttpRequest(this.request))
.replacePath(path)
.build().normalize().toUriString();
super.sendRedirect(result);
}
}
}