diff --git a/openid/src/main/java/org/springframework/security/openid/OpenIDAuthenticationProcessingFilter.java b/openid/src/main/java/org/springframework/security/openid/OpenIDAuthenticationProcessingFilter.java index 7eb6208841..94f5f5d17e 100644 --- a/openid/src/main/java/org/springframework/security/openid/OpenIDAuthenticationProcessingFilter.java +++ b/openid/src/main/java/org/springframework/security/openid/OpenIDAuthenticationProcessingFilter.java @@ -167,15 +167,17 @@ public class OpenIDAuthenticationProcessingFilter extends AbstractAuthentication if (mapping == null) { try { - URL url = new URL(returnToUrl); - int port = (url.getPort() == -1) ? 80 : url.getPort(); - StringBuffer realmBuffer = new StringBuffer(returnToUrl.length()) + int port = url.getPort(); + + StringBuilder realmBuffer = new StringBuilder(returnToUrl.length()) .append(url.getProtocol()) .append("://") - .append(url.getHost()) - .append(":").append(port) - .append("/"); + .append(url.getHost()); + if (port > 0) { + realmBuffer.append(":").append(port); + } + realmBuffer.append("/"); mapping = realmBuffer.toString(); } catch (MalformedURLException e) { logger.warn("returnToUrl was not a valid URL: [" + returnToUrl + "]", e); diff --git a/web/src/main/java/org/springframework/security/web/DefaultRedirectStrategy.java b/web/src/main/java/org/springframework/security/web/DefaultRedirectStrategy.java new file mode 100644 index 0000000000..c968218b98 --- /dev/null +++ b/web/src/main/java/org/springframework/security/web/DefaultRedirectStrategy.java @@ -0,0 +1,58 @@ +package org.springframework.security.web; + +import java.io.IOException; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * Simple implementation of RedirectStrategy which is the default used throughout the framework. + * + * @author Luke Taylor + * @version $Id$ + * @since 3.0 + */ +public class DefaultRedirectStrategy implements RedirectStrategy { + private boolean useRelativeContext; + + /** + * Redirects the response to the supplied URL. + *
+ * If useRelativeContext is set, the redirect value will be the value after the request context path. + */ + public void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url) throws IOException { + String finalUrl; + if (!url.startsWith("http://") && !url.startsWith("https://")) { + if (useRelativeContext) { + finalUrl = url; + } + else { + finalUrl = request.getContextPath() + url; + } + } + else if (useRelativeContext) { + // Calculate the relative URL from the fully qualifed URL, minus the protocol and base context. + int len = request.getContextPath().length(); + int index = url.indexOf(request.getContextPath()) + len; + finalUrl = url.substring(index); + + if (finalUrl.length() > 1 && finalUrl.charAt(0) == '/') { + finalUrl = finalUrl.substring(1); + } + } + else { + finalUrl = url; + } + + response.sendRedirect(response.encodeRedirectURL(finalUrl)); + } + + /** + * If true, causes any redirection URLs to be calculated minus the protocol + * and context path (defaults to false). + */ + public void setUseRelativeContext(boolean useRelativeContext) { + this.useRelativeContext = useRelativeContext; + } + +} diff --git a/web/src/main/java/org/springframework/security/web/RedirectStrategy.java b/web/src/main/java/org/springframework/security/web/RedirectStrategy.java new file mode 100644 index 0000000000..f68e0c7bbb --- /dev/null +++ b/web/src/main/java/org/springframework/security/web/RedirectStrategy.java @@ -0,0 +1,24 @@ +package org.springframework.security.web; + +import java.io.IOException; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * Encapsulates the redirection logic for all classes in the framework which perform redirects. + * + * @author Luke Taylor + * @version $Id$ + * @since 3.0 + */ +public interface RedirectStrategy { + + /** + * Performs a redirect to the supplied URL + * @param request the current request + * @param response the response to redirect + * @param url the target URL to redirect to, for example "/login" + */ + void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url) throws IOException; +} diff --git a/web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationTargetUrlRequestHandler.java b/web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationTargetUrlRequestHandler.java index 642d3311b7..4994696a98 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationTargetUrlRequestHandler.java +++ b/web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationTargetUrlRequestHandler.java @@ -11,7 +11,8 @@ import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.security.core.Authentication; -import org.springframework.security.web.util.RedirectUtils; +import org.springframework.security.web.DefaultRedirectStrategy; +import org.springframework.security.web.RedirectStrategy; import org.springframework.security.web.util.UrlUtils; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -50,8 +51,8 @@ public abstract class AbstractAuthenticationTargetUrlRequestHandler { private String targetUrlParameter = DEFAULT_TARGET_PARAMETER; private String defaultTargetUrl = "/"; private boolean alwaysUseDefaultTargetUrl = false; - private boolean useRelativeContext = false; private boolean useReferer = false; + private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); protected AbstractAuthenticationTargetUrlRequestHandler() { } @@ -60,7 +61,7 @@ public abstract class AbstractAuthenticationTargetUrlRequestHandler { throws IOException, ServletException { String targetUrl = determineTargetUrl(request, response); - RedirectUtils.sendRedirect(request, response, targetUrl, useRelativeContext); + redirectStrategy.sendRedirect(request, response, targetUrl); } private String determineTargetUrl(HttpServletRequest request, HttpServletResponse response) { @@ -149,15 +150,14 @@ public abstract class AbstractAuthenticationTargetUrlRequestHandler { } /** - * If true, causes any redirection URLs to be calculated minus the protocol - * and context path (defaults to false). + * Allows overriding of the behaviour when redirecting to a target URL. */ - public void setUseRelativeContext(boolean useRelativeContext) { - this.useRelativeContext = useRelativeContext; + public void setRedirectStrategy(RedirectStrategy redirectStrategy) { + this.redirectStrategy = redirectStrategy; } - protected boolean isUseRelativeContext() { - return useRelativeContext; + protected RedirectStrategy getRedirectStrategy() { + return redirectStrategy; } /** diff --git a/web/src/main/java/org/springframework/security/web/authentication/ExceptionMappingAuthenticationFailureHandler.java b/web/src/main/java/org/springframework/security/web/authentication/ExceptionMappingAuthenticationFailureHandler.java index a51e39cb2c..311b76baef 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/ExceptionMappingAuthenticationFailureHandler.java +++ b/web/src/main/java/org/springframework/security/web/authentication/ExceptionMappingAuthenticationFailureHandler.java @@ -9,7 +9,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.util.RedirectUtils; import org.springframework.security.web.util.UrlUtils; import org.springframework.util.Assert; @@ -35,7 +34,7 @@ public class ExceptionMappingAuthenticationFailureHandler extends SimpleUrlAuthe String url = failureUrlMap.get(exception.getClass().getName()); if (url != null) { - RedirectUtils.sendRedirect(request, response, url, isUseRelativeContext()); + getRedirectStrategy().sendRedirect(request, response, url); } else { super.onAuthenticationFailure(request, response, exception); } diff --git a/web/src/main/java/org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.java b/web/src/main/java/org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.java index 35768f074a..c6a7fcf1ce 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.java +++ b/web/src/main/java/org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.java @@ -17,24 +17,6 @@ package org.springframework.security.web.authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.security.web.PortMapper; -import org.springframework.security.web.PortMapperImpl; -import org.springframework.security.web.PortResolver; -import org.springframework.security.web.PortResolverImpl; -import org.springframework.security.web.access.ExceptionTranslationFilter; -import org.springframework.security.web.util.RedirectUrlBuilder; -import org.springframework.security.web.util.UrlUtils; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.beans.factory.InitializingBean; - -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; - import java.io.IOException; import javax.servlet.RequestDispatcher; @@ -42,6 +24,23 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.security.web.DefaultRedirectStrategy; +import org.springframework.security.web.PortMapper; +import org.springframework.security.web.PortMapperImpl; +import org.springframework.security.web.PortResolver; +import org.springframework.security.web.PortResolverImpl; +import org.springframework.security.web.RedirectStrategy; +import org.springframework.security.web.access.ExceptionTranslationFilter; +import org.springframework.security.web.util.RedirectUrlBuilder; +import org.springframework.security.web.util.UrlUtils; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + /** * Used by the {@link ExceptionTranslationFilter} to commence a form login * authentication via the {@link UsernamePasswordAuthenticationProcessingFilter}. This object @@ -80,6 +79,8 @@ public class LoginUrlAuthenticationEntryPoint implements AuthenticationEntryPoin private boolean useForward = false; + private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); + //~ Methods ======================================================================================================== public void afterPropertiesSet() throws Exception { @@ -117,6 +118,8 @@ public class LoginUrlAuthenticationEntryPoint implements AuthenticationEntryPoin if (useForward) { if (forceHttps && "http".equals(request.getScheme())) { + // First redirect the current request to HTTPS. + // When that request is received, the forward to the login page will be used. redirectUrl = buildHttpsRedirectUrlForRequest(httpRequest); } @@ -140,7 +143,7 @@ public class LoginUrlAuthenticationEntryPoint implements AuthenticationEntryPoin } - httpResponse.sendRedirect(httpResponse.encodeRedirectURL(redirectUrl)); + redirectStrategy.sendRedirect(httpRequest, httpResponse, redirectUrl); } protected String buildRedirectUrlToLoginPage(HttpServletRequest request, HttpServletResponse response, @@ -174,7 +177,8 @@ public class LoginUrlAuthenticationEntryPoint implements AuthenticationEntryPoin } /** - * Builds a URL to redirect the supplied request to HTTPS. + * Builds a URL to redirect the supplied request to HTTPS. Used to redirect the current request + * to HTTPS, before doing a forward to the login page. */ protected String buildHttpsRedirectUrlForRequest(HttpServletRequest request) throws IOException, ServletException { diff --git a/web/src/main/java/org/springframework/security/web/authentication/SavedRequestAwareAuthenticationSuccessHandler.java b/web/src/main/java/org/springframework/security/web/authentication/SavedRequestAwareAuthenticationSuccessHandler.java index 000770a24f..6b6c252c8d 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/SavedRequestAwareAuthenticationSuccessHandler.java +++ b/web/src/main/java/org/springframework/security/web/authentication/SavedRequestAwareAuthenticationSuccessHandler.java @@ -13,7 +13,6 @@ import org.springframework.security.web.access.ExceptionTranslationFilter; import org.springframework.security.web.savedrequest.HttpSessionRequestCache; import org.springframework.security.web.savedrequest.RequestCache; import org.springframework.security.web.savedrequest.SavedRequest; -import org.springframework.security.web.util.RedirectUtils; import org.springframework.util.StringUtils; /** @@ -76,7 +75,7 @@ public class SavedRequestAwareAuthenticationSuccessHandler extends SimpleUrlAuth // Use the SavedRequest URL String targetUrl = savedRequest.getFullRequestUrl(); logger.debug("Redirecting to SavedRequest Url: " + targetUrl); - RedirectUtils.sendRedirect(request, response, targetUrl, isUseRelativeContext()); + getRedirectStrategy().sendRedirect(request, response, targetUrl); } public void setRequestCache(RequestCache requestCache) { diff --git a/web/src/main/java/org/springframework/security/web/authentication/SimpleUrlAuthenticationFailureHandler.java b/web/src/main/java/org/springframework/security/web/authentication/SimpleUrlAuthenticationFailureHandler.java index 5b86f4f453..f7425731c4 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/SimpleUrlAuthenticationFailureHandler.java +++ b/web/src/main/java/org/springframework/security/web/authentication/SimpleUrlAuthenticationFailureHandler.java @@ -7,7 +7,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.util.RedirectUtils; +import org.springframework.security.web.DefaultRedirectStrategy; +import org.springframework.security.web.RedirectStrategy; import org.springframework.security.web.util.UrlUtils; import org.springframework.util.Assert; @@ -27,7 +28,7 @@ import org.springframework.util.Assert; public class SimpleUrlAuthenticationFailureHandler implements AuthenticationFailureHandler { private String defaultFailureUrl; private boolean forwardToDestination = false; - private boolean useRelativeContext = false; + private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); public SimpleUrlAuthenticationFailureHandler() { } @@ -44,7 +45,7 @@ public class SimpleUrlAuthenticationFailureHandler implements AuthenticationFail if (forwardToDestination) { request.getRequestDispatcher(defaultFailureUrl).forward(request, response); } else { - RedirectUtils.sendRedirect(request, response, defaultFailureUrl, useRelativeContext); + redirectStrategy.sendRedirect(request, response, defaultFailureUrl); } } } @@ -71,16 +72,14 @@ public class SimpleUrlAuthenticationFailureHandler implements AuthenticationFail this.forwardToDestination = forwardToDestination; } - protected boolean isUseRelativeContext() { - return useRelativeContext; - } - /** - * If true, causes any redirection URLs to be calculated minus the protocol - * and context path (defaults to false). + * Allows overriding of the behaviour when redirecting to a target URL. */ - public void setUseRelativeContext(boolean useRelativeContext) { - this.useRelativeContext = useRelativeContext; + public void setRedirectStrategy(RedirectStrategy redirectStrategy) { + this.redirectStrategy = redirectStrategy; } + protected RedirectStrategy getRedirectStrategy() { + return redirectStrategy; + } } diff --git a/web/src/main/java/org/springframework/security/web/util/RedirectUtils.java b/web/src/main/java/org/springframework/security/web/util/RedirectUtils.java deleted file mode 100644 index ab87d1e4c5..0000000000 --- a/web/src/main/java/org/springframework/security/web/util/RedirectUtils.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.springframework.security.web.util; - -import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; -import org.springframework.security.web.authentication.logout.LogoutFilter; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * @author Luke Taylor - * @version $Id$ - */ -public abstract class RedirectUtils { - //~ Constructors =================================================================================================== - - private RedirectUtils() { - } - - //~ Methods ======================================================================================================== - - /** - * Encapsulates the redirect logic used in classes like {@link AbstractAuthenticationProcessingFilter} and {@link LogoutFilter}. - * - * @param request the incoming request - * @param response the response to redirect - * @param url the target url to redirect to - * @param useRelativeContext if true, causes any redirection URLs to be calculated minus the protocol - * and context path. - * - * @see AbstractAuthenticationProcessingFilter#setUseRelativeContext(boolean) - */ - public static final void sendRedirect(HttpServletRequest request, - HttpServletResponse response, - String url, - boolean useRelativeContext) throws IOException { - String finalUrl; - if (!url.startsWith("http://") && !url.startsWith("https://")) { - if (useRelativeContext) { - finalUrl = url; - } - else { - finalUrl = request.getContextPath() + url; - } - } - else if (useRelativeContext) { - // Calculate the relative URL from the fully qualifed URL, minus the protocol and base context. - int len = request.getContextPath().length(); - int index = url.indexOf(request.getContextPath()) + len; - finalUrl = url.substring(index); - - if (finalUrl.length() > 1 && finalUrl.charAt(0) == '/') { - finalUrl = finalUrl.substring(1); - } - } - else { - finalUrl = url; - } - - response.sendRedirect(response.encodeRedirectURL(finalUrl)); - } -} diff --git a/web/src/test/java/org/springframework/security/web/authentication/switchuser/SwitchUserProcessingFilterTests.java b/web/src/test/java/org/springframework/security/web/authentication/switchuser/SwitchUserProcessingFilterTests.java index 1c4a96918e..25ff3f5744 100644 --- a/web/src/test/java/org/springframework/security/web/authentication/switchuser/SwitchUserProcessingFilterTests.java +++ b/web/src/test/java/org/springframework/security/web/authentication/switchuser/SwitchUserProcessingFilterTests.java @@ -43,6 +43,7 @@ import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.util.FieldUtils; +import org.springframework.security.web.DefaultRedirectStrategy; import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; import org.springframework.security.web.authentication.switchuser.SwitchUserAuthorityChanger; import org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority; @@ -306,7 +307,9 @@ public class SwitchUserProcessingFilterTests { filter.setSwitchUserUrl("/j_spring_security_switch_user"); SimpleUrlAuthenticationSuccessHandler switchSuccessHandler = new SimpleUrlAuthenticationSuccessHandler("/someOtherUrl"); - switchSuccessHandler.setUseRelativeContext(true); + DefaultRedirectStrategy contextRelativeRedirector = new DefaultRedirectStrategy(); + contextRelativeRedirector.setUseRelativeContext(true); + switchSuccessHandler.setRedirectStrategy(contextRelativeRedirector); filter.setSuccessHandler(switchSuccessHandler); filter.setUserDetailsService(new MockUserDetailsService());