@@ -104,6 +104,8 @@ public class RedirectView extends AbstractUrlBasedView implements SmartView {
104
104
105
105
private boolean propagateQueryParams = false ;
106
106
107
+ private String [] hosts ;
108
+
107
109
108
110
/**
109
111
* Constructor for use as a bean.
@@ -252,6 +254,27 @@ public boolean isPropagateQueryProperties() {
252
254
return this .propagateQueryParams ;
253
255
}
254
256
257
+ /**
258
+ * Configure one or more hosts associated with the application. All other
259
+ * hosts will be considered external hosts. In effect this property
260
+ * provides a way turn off encoding via
261
+ * {@link HttpServletResponse#encodeRedirectURL} for URLs that have a host
262
+ * and that host is not listed as a known host.
263
+ * <p>If not set (the default) all URLs are encoded through the response.
264
+ * @param hosts one or more application hosts
265
+ * @since 4.3
266
+ */
267
+ public void setHosts (String [] hosts ) {
268
+ this .hosts = hosts ;
269
+ }
270
+
271
+ /**
272
+ * Return the configured application hosts.
273
+ */
274
+ public String [] getHosts () {
275
+ return this .hosts ;
276
+ }
277
+
255
278
/**
256
279
* Returns "true" indicating this view performs a redirect.
257
280
*/
@@ -583,29 +606,55 @@ protected String updateTargetUrl(String targetUrl, Map<String, Object> model,
583
606
protected void sendRedirect (HttpServletRequest request , HttpServletResponse response ,
584
607
String targetUrl , boolean http10Compatible ) throws IOException {
585
608
586
- String encodedRedirectURL = response .encodeRedirectURL (targetUrl );
609
+ String encodedURL = ( isRemoteHost ( targetUrl ) ? targetUrl : response .encodeRedirectURL (targetUrl ) );
587
610
if (http10Compatible ) {
588
611
HttpStatus attributeStatusCode = (HttpStatus ) request .getAttribute (View .RESPONSE_STATUS_ATTRIBUTE );
589
612
if (this .statusCode != null ) {
590
613
response .setStatus (this .statusCode .value ());
591
- response .setHeader ("Location" , encodedRedirectURL );
614
+ response .setHeader ("Location" , encodedURL );
592
615
}
593
616
else if (attributeStatusCode != null ) {
594
617
response .setStatus (attributeStatusCode .value ());
595
- response .setHeader ("Location" , encodedRedirectURL );
618
+ response .setHeader ("Location" , encodedURL );
596
619
}
597
620
else {
598
621
// Send status code 302 by default.
599
- response .sendRedirect (encodedRedirectURL );
622
+ response .sendRedirect (encodedURL );
600
623
}
601
624
}
602
625
else {
603
626
HttpStatus statusCode = getHttp11StatusCode (request , response , targetUrl );
604
627
response .setStatus (statusCode .value ());
605
- response .setHeader ("Location" , encodedRedirectURL );
628
+ response .setHeader ("Location" , encodedURL );
606
629
}
607
630
}
608
631
632
+ /**
633
+ * Whether the given targetUrl has a host that is a "foreign" system in which
634
+ * case {@link HttpServletResponse#encodeRedirectURL} will not be applied.
635
+ * This method returns {@code true} if the {@link #setHosts(String[])}
636
+ * property is configured and the target URL has a host that does not match.
637
+ * @param targetUrl the target redirect URL
638
+ * @return {@code true} the target URL has a remote host, {@code false} if it
639
+ * the URL does not have a host or the "host" property is not configured.
640
+ * @since 4.3
641
+ */
642
+ protected boolean isRemoteHost (String targetUrl ) {
643
+ if (ObjectUtils .isEmpty (getHosts ())) {
644
+ return false ;
645
+ }
646
+ String targetHost = UriComponentsBuilder .fromUriString (targetUrl ).build ().getHost ();
647
+ if (StringUtils .isEmpty (targetHost )) {
648
+ return false ;
649
+ }
650
+ for (String host : getHosts ()) {
651
+ if (targetHost .equals (host )) {
652
+ return false ;
653
+ }
654
+ }
655
+ return true ;
656
+ }
657
+
609
658
/**
610
659
* Determines the status code to use for HTTP 1.1 compatible requests.
611
660
* <p>The default implementation returns the {@link #setStatusCode(HttpStatus) statusCode}
0 commit comments