4040import org .springframework .web .util .UrlPathHelper ;
4141
4242/**
43- * Filter that wraps the request and response in order to override its
43+ * Extract values from "Forwarded" and "X-Forwarded-*" headers in order to wrap
44+ * and override the following from the request and response:
4445 * {@link HttpServletRequest#getServerName() getServerName()},
4546 * {@link HttpServletRequest#getServerPort() getServerPort()},
4647 * {@link HttpServletRequest#getScheme() getScheme()},
47- * {@link HttpServletRequest#isSecure() isSecure()},
48- * {@link HttpServletResponse#sendRedirect(String) sendRedirect(String)},
49- * methods with values derived from "Forwarded" or "X-Forwarded-*"
50- * headers. In effect the wrapped request and response reflects the
51- * client-originated protocol and address.
48+ * {@link HttpServletRequest#isSecure() isSecure()}, and
49+ * {@link HttpServletResponse#sendRedirect(String) sendRedirect(String)}.
50+ * In effect the wrapped request and response reflect the client-originated
51+ * protocol and address.
52+ *
53+ * <p><strong>Note:</strong> This filter can also be used in a
54+ * {@link #setRemoveOnly removeOnly} mode where "Forwarded" and "X-Forwarded-*"
55+ * headers are only eliminated without being used.
5256 *
5357 * @author Rossen Stoyanchev
5458 * @author Eddú Meléndez
5559 * @author Rob Winch
5660 * @since 4.3
61+ * @see <a href="https://tools.ietf.org/html/rfc7239">https://tools.ietf.org/html/rfc7239</a>
5762 */
5863public class ForwardedHeaderFilter extends OncePerRequestFilter {
5964
@@ -71,6 +76,8 @@ public class ForwardedHeaderFilter extends OncePerRequestFilter {
7176
7277 private final UrlPathHelper pathHelper ;
7378
79+ private boolean removeOnly ;
80+
7481
7582 public ForwardedHeaderFilter () {
7683 this .pathHelper = new UrlPathHelper ();
@@ -79,6 +86,17 @@ public ForwardedHeaderFilter() {
7986 }
8087
8188
89+ /**
90+ * Enables mode in which any "Forwarded" or "X-Forwarded-*" headers are
91+ * removed only and the information in them ignored.
92+ * @param removeOnly whether to discard and ingore forwarded headers
93+ * @since 4.3.9
94+ */
95+ public void setRemoveOnly (boolean removeOnly ) {
96+ this .removeOnly = removeOnly ;
97+ }
98+
99+
82100 @ Override
83101 protected boolean shouldNotFilter (HttpServletRequest request ) throws ServletException {
84102 Enumeration <String > names = request .getHeaderNames ();
@@ -105,13 +123,67 @@ protected boolean shouldNotFilterErrorDispatch() {
105123 protected void doFilterInternal (HttpServletRequest request , HttpServletResponse response ,
106124 FilterChain filterChain ) throws ServletException , IOException {
107125
108- ForwardedHeaderRequestWrapper wrappedRequest = new ForwardedHeaderRequestWrapper (request , this .pathHelper );
109- ForwardedHeaderResponseWrapper wrappedResponse = new ForwardedHeaderResponseWrapper (response , wrappedRequest );
110- filterChain .doFilter (wrappedRequest , wrappedResponse );
126+ if (this .removeOnly ) {
127+ ForwardedHeaderRemovingRequest theRequest = new ForwardedHeaderRemovingRequest (request );
128+ filterChain .doFilter (theRequest , response );
129+ }
130+ else {
131+ HttpServletRequest theRequest = new ForwardedHeaderExtractingRequest (request , this .pathHelper );
132+ HttpServletResponse theResponse = new ForwardedHeaderExtractingResponse (response , theRequest );
133+ filterChain .doFilter (theRequest , theResponse );
134+ }
111135 }
112136
113137
114- private static class ForwardedHeaderRequestWrapper extends HttpServletRequestWrapper {
138+ /**
139+ * Hide "Forwarded" or "X-Forwarded-*" headers.
140+ */
141+ private static class ForwardedHeaderRemovingRequest extends HttpServletRequestWrapper {
142+
143+ private final Map <String , List <String >> headers ;
144+
145+
146+ public ForwardedHeaderRemovingRequest (HttpServletRequest request ) {
147+ super (request );
148+ this .headers = initHeaders (request );
149+ }
150+
151+ private static Map <String , List <String >> initHeaders (HttpServletRequest request ) {
152+ Map <String , List <String >> headers = new LinkedCaseInsensitiveMap <>(Locale .ENGLISH );
153+ Enumeration <String > names = request .getHeaderNames ();
154+ while (names .hasMoreElements ()) {
155+ String name = names .nextElement ();
156+ if (!FORWARDED_HEADER_NAMES .contains (name )) {
157+ headers .put (name , Collections .list (request .getHeaders (name )));
158+ }
159+ }
160+ return headers ;
161+ }
162+
163+ // Override header accessors to not expose forwarded headers
164+
165+ @ Override
166+ public String getHeader (String name ) {
167+ List <String > value = this .headers .get (name );
168+ return (CollectionUtils .isEmpty (value ) ? null : value .get (0 ));
169+ }
170+
171+ @ Override
172+ public Enumeration <String > getHeaders (String name ) {
173+ List <String > value = this .headers .get (name );
174+ return (Collections .enumeration (value != null ? value : Collections .emptySet ()));
175+ }
176+
177+ @ Override
178+ public Enumeration <String > getHeaderNames () {
179+ return Collections .enumeration (this .headers .keySet ());
180+ }
181+ }
182+
183+ /**
184+ * Extract and use "Forwarded" or "X-Forwarded-*" headers.
185+ */
186+ private static class ForwardedHeaderExtractingRequest extends ForwardedHeaderRemovingRequest {
115187
116188 private final String scheme ;
117189
@@ -127,9 +199,8 @@ private static class ForwardedHeaderRequestWrapper extends HttpServletRequestWra
127199
128200 private final String requestUrl ;
129201
130- private final Map <String , List <String >> headers ;
131202
132- public ForwardedHeaderRequestWrapper (HttpServletRequest request , UrlPathHelper pathHelper ) {
203+ public ForwardedHeaderExtractingRequest (HttpServletRequest request , UrlPathHelper pathHelper ) {
133204 super (request );
134205
135206 HttpRequest httpRequest = new ServletServerHttpRequest (request );
@@ -145,7 +216,6 @@ public ForwardedHeaderRequestWrapper(HttpServletRequest request, UrlPathHelper p
145216 this .contextPath = (prefix != null ? prefix : request .getContextPath ());
146217 this .requestUri = this .contextPath + pathHelper .getPathWithinApplication (request );
147218 this .requestUrl = this .scheme + "://" + this .host + (port == -1 ? "" : ":" + port ) + this .requestUri ;
148- this .headers = initHeaders (request );
149219 }
150220
151221 private static String getForwardedPrefix (HttpServletRequest request ) {
@@ -165,21 +235,6 @@ private static String getForwardedPrefix(HttpServletRequest request) {
165235 return prefix ;
166236 }
167237
168- /**
169- * Copy the headers excluding any {@link #FORWARDED_HEADER_NAMES}.
170- */
171- private static Map <String , List <String >> initHeaders (HttpServletRequest request ) {
172- Map <String , List <String >> headers = new LinkedCaseInsensitiveMap <>(Locale .ENGLISH );
173- Enumeration <String > names = request .getHeaderNames ();
174- while (names .hasMoreElements ()) {
175- String name = names .nextElement ();
176- if (!FORWARDED_HEADER_NAMES .contains (name )) {
177- headers .put (name , Collections .list (request .getHeaders (name )));
178- }
179- }
180- return headers ;
181- }
182-
183238 @ Override
184239 public String getScheme () {
185240 return this .scheme ;
@@ -214,35 +269,18 @@ public String getRequestURI() {
214269 public StringBuffer getRequestURL () {
215270 return new StringBuffer (this .requestUrl );
216271 }
217-
218- // Override header accessors to not expose forwarded headers
219-
220- @ Override
221- public String getHeader (String name ) {
222- List <String > value = this .headers .get (name );
223- return (CollectionUtils .isEmpty (value ) ? null : value .get (0 ));
224- }
225-
226- @ Override
227- public Enumeration <String > getHeaders (String name ) {
228- List <String > value = this .headers .get (name );
229- return (Collections .enumeration (value != null ? value : Collections .emptySet ()));
230- }
231-
232- @ Override
233- public Enumeration <String > getHeaderNames () {
234- return Collections .enumeration (this .headers .keySet ());
235- }
236272 }
237273
238274
239- private static class ForwardedHeaderResponseWrapper extends HttpServletResponseWrapper {
275+ private static class ForwardedHeaderExtractingResponse extends HttpServletResponseWrapper {
240276
241277 private static final String FOLDER_SEPARATOR = "/" ;
242278
279+
243280 private final HttpServletRequest request ;
244281
245- public ForwardedHeaderResponseWrapper (HttpServletResponse response , HttpServletRequest request ) {
282+
283+ public ForwardedHeaderExtractingResponse (HttpServletResponse response , HttpServletRequest request ) {
246284 super (response );
247285 this .request = request ;
248286 }
0 commit comments