diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapter.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapter.java index 5e2e9697..66fbd8d1 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapter.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2012 the original author or authors. + * Copyright 2004-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,6 +28,8 @@ import org.springframework.http.HttpStatus; import org.springframework.js.ajax.AjaxHandler; import org.springframework.js.ajax.SpringJavascriptAjaxHandler; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; import org.springframework.web.servlet.FlashMap; import org.springframework.web.servlet.FlashMapManager; import org.springframework.web.servlet.HandlerAdapter; @@ -87,6 +89,8 @@ public class FlowHandlerAdapter extends WebContentGenerator implements HandlerAd private HttpStatus statusCode; + private String[] hosts; + private boolean saveOutputToFlashScopeOnRedirect; /** @@ -178,6 +182,27 @@ public class FlowHandlerAdapter extends WebContentGenerator implements HandlerAd this.statusCode = statusCode; } + /** + * Configure one or more hosts associated with the application. All other + * hosts will be considered external hosts. In effect this property + * provides a way turn off encoding via + * {@link HttpServletResponse#encodeRedirectURL} for URLs that have a host + * and that host is not listed as a known host. + *

If not set (the default) all URLs are encoded through the response. + * @param hosts one or more application hosts + * @since 2.4.3 + */ + public void setHosts(String[] hosts) { + this.hosts = hosts; + } + + /** + * Return the configured hosts associated with the application. + */ + public String[] getHosts() { + return this.hosts; + } + /** * Set whether servlet relative redirects sent by this handler adapter * should pass {@link FlowExecutionOutcome#getOutput() flow output} to the @@ -357,7 +382,7 @@ public class FlowHandlerAdapter extends WebContentGenerator implements HandlerAd if (ajaxHandler.isAjaxRequest(request, response)) { ajaxHandler.sendAjaxRedirect(url, request, response, false); } else { - String encodedRedirectURL = response.encodeRedirectURL(url); + String encodedRedirectURL = (isRemoteHost(url) ? url : response.encodeRedirectURL(url)); if (redirectHttp10Compatible) { if (statusCode != null) { response.setStatus(statusCode.value()); @@ -376,6 +401,32 @@ public class FlowHandlerAdapter extends WebContentGenerator implements HandlerAd } } + /** + * Whether the given targetUrl has a host that is a "foreign" system in which + * case {@link HttpServletResponse#encodeRedirectURL} will not be applied. + * This method returns {@code true} if the {@link #setHosts(String[])} + * property is configured and the target URL has a host that does not match. + * @param targetUrl the target redirect URL + * @return {@code true} the target URL has a remote host, {@code false} if it + * the URL does not have a host or the "host" property is not configured. + * @since 2.4.3 + */ + protected boolean isRemoteHost(String targetUrl) { + if (ObjectUtils.isEmpty(getHosts())) { + return false; + } + String targetHost = UriComponentsBuilder.fromUriString(targetUrl).build().getHost(); + if (StringUtils.isEmpty(targetHost)) { + return false; + } + for (String host : getHosts()) { + if (targetHost.equals(host)) { + return false; + } + } + return true; + } + // internal helpers private void handleFlowExecutionResult(FlowExecutionResult result, ServletExternalContext context, diff --git a/spring-webflow/src/test/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapterTests.java b/spring-webflow/src/test/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapterTests.java index a292268e..421ec50f 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapterTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapterTests.java @@ -387,6 +387,19 @@ public class FlowHandlerAdapterTests extends TestCase { EasyMock.verify(new Object[] { flowExecutor }); } + public void testRemoteHost() throws Exception { + assertFalse(flowHandlerAdapter.isRemoteHost("http://url.somewhere.com")); + assertFalse(flowHandlerAdapter.isRemoteHost("/path")); + assertFalse(flowHandlerAdapter.isRemoteHost("http://url.somewhereelse.com")); + + flowHandlerAdapter.setHosts(new String[] {"url.somewhere.com"}); + + assertFalse(flowHandlerAdapter.isRemoteHost("http://url.somewhere.com")); + assertFalse(flowHandlerAdapter.isRemoteHost("/path")); + assertTrue(flowHandlerAdapter.isRemoteHost("http://url.somewhereelse.com")); + + } + public void testDefaultHandleFlowException() throws Exception { setupRequest("/springtravel", "/app", "/foo", "GET"); Map parameters = new HashMap();