40
40
import org .springframework .web .util .UrlPathHelper ;
41
41
42
42
/**
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:
44
45
* {@link HttpServletRequest#getServerName() getServerName()},
45
46
* {@link HttpServletRequest#getServerPort() getServerPort()},
46
47
* {@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.
52
56
*
53
57
* @author Rossen Stoyanchev
54
58
* @author Eddú Meléndez
55
59
* @author Rob Winch
56
60
* @since 4.3
61
+ * @see <a href="https://tools.ietf.org/html/rfc7239">https://tools.ietf.org/html/rfc7239</a>
57
62
*/
58
63
public class ForwardedHeaderFilter extends OncePerRequestFilter {
59
64
@@ -71,6 +76,8 @@ public class ForwardedHeaderFilter extends OncePerRequestFilter {
71
76
72
77
private final UrlPathHelper pathHelper ;
73
78
79
+ private boolean removeOnly ;
80
+
74
81
75
82
public ForwardedHeaderFilter () {
76
83
this .pathHelper = new UrlPathHelper ();
@@ -79,6 +86,17 @@ public ForwardedHeaderFilter() {
79
86
}
80
87
81
88
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
+
82
100
@ Override
83
101
protected boolean shouldNotFilter (HttpServletRequest request ) throws ServletException {
84
102
Enumeration <String > names = request .getHeaderNames ();
@@ -105,13 +123,67 @@ protected boolean shouldNotFilterErrorDispatch() {
105
123
protected void doFilterInternal (HttpServletRequest request , HttpServletResponse response ,
106
124
FilterChain filterChain ) throws ServletException , IOException {
107
125
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
+ }
111
135
}
112
136
113
137
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 {
115
187
116
188
private final String scheme ;
117
189
@@ -127,9 +199,8 @@ private static class ForwardedHeaderRequestWrapper extends HttpServletRequestWra
127
199
128
200
private final String requestUrl ;
129
201
130
- private final Map <String , List <String >> headers ;
131
202
132
- public ForwardedHeaderRequestWrapper (HttpServletRequest request , UrlPathHelper pathHelper ) {
203
+ public ForwardedHeaderExtractingRequest (HttpServletRequest request , UrlPathHelper pathHelper ) {
133
204
super (request );
134
205
135
206
HttpRequest httpRequest = new ServletServerHttpRequest (request );
@@ -145,7 +216,6 @@ public ForwardedHeaderRequestWrapper(HttpServletRequest request, UrlPathHelper p
145
216
this .contextPath = (prefix != null ? prefix : request .getContextPath ());
146
217
this .requestUri = this .contextPath + pathHelper .getPathWithinApplication (request );
147
218
this .requestUrl = this .scheme + "://" + this .host + (port == -1 ? "" : ":" + port ) + this .requestUri ;
148
- this .headers = initHeaders (request );
149
219
}
150
220
151
221
private static String getForwardedPrefix (HttpServletRequest request ) {
@@ -165,21 +235,6 @@ private static String getForwardedPrefix(HttpServletRequest request) {
165
235
return prefix ;
166
236
}
167
237
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
-
183
238
@ Override
184
239
public String getScheme () {
185
240
return this .scheme ;
@@ -214,35 +269,18 @@ public String getRequestURI() {
214
269
public StringBuffer getRequestURL () {
215
270
return new StringBuffer (this .requestUrl );
216
271
}
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
- }
236
272
}
237
273
238
274
239
- private static class ForwardedHeaderResponseWrapper extends HttpServletResponseWrapper {
275
+ private static class ForwardedHeaderExtractingResponse extends HttpServletResponseWrapper {
240
276
241
277
private static final String FOLDER_SEPARATOR = "/" ;
242
278
279
+
243
280
private final HttpServletRequest request ;
244
281
245
- public ForwardedHeaderResponseWrapper (HttpServletResponse response , HttpServletRequest request ) {
282
+
283
+ public ForwardedHeaderExtractingResponse (HttpServletResponse response , HttpServletRequest request ) {
246
284
super (response );
247
285
this .request = request ;
248
286
}
0 commit comments