SEC-1226: Introduce RedirectStrategy to replace RedirectUtils. Implemented strategy and applied throughout relevant classes.

This commit is contained in:
Luke Taylor 2009-08-27 10:42:11 +00:00
parent 092d7b5c2b
commit ab0d66071a
10 changed files with 139 additions and 113 deletions

View File

@ -167,15 +167,17 @@ public class OpenIDAuthenticationProcessingFilter extends AbstractAuthentication
if (mapping == null) { if (mapping == null) {
try { try {
URL url = new URL(returnToUrl); URL url = new URL(returnToUrl);
int port = (url.getPort() == -1) ? 80 : url.getPort(); int port = url.getPort();
StringBuffer realmBuffer = new StringBuffer(returnToUrl.length())
StringBuilder realmBuffer = new StringBuilder(returnToUrl.length())
.append(url.getProtocol()) .append(url.getProtocol())
.append("://") .append("://")
.append(url.getHost()) .append(url.getHost());
.append(":").append(port) if (port > 0) {
.append("/"); realmBuffer.append(":").append(port);
}
realmBuffer.append("/");
mapping = realmBuffer.toString(); mapping = realmBuffer.toString();
} catch (MalformedURLException e) { } catch (MalformedURLException e) {
logger.warn("returnToUrl was not a valid URL: [" + returnToUrl + "]", e); logger.warn("returnToUrl was not a valid URL: [" + returnToUrl + "]", e);

View File

@ -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 <tt>RedirectStrategy</tt> 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.
* <p>
* If <tt>useRelativeContext</tt> 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 <tt>true</tt>, causes any redirection URLs to be calculated minus the protocol
* and context path (defaults to <tt>false</tt>).
*/
public void setUseRelativeContext(boolean useRelativeContext) {
this.useRelativeContext = useRelativeContext;
}
}

View File

@ -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;
}

View File

@ -11,7 +11,8 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.security.core.Authentication; 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.security.web.util.UrlUtils;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -50,8 +51,8 @@ public abstract class AbstractAuthenticationTargetUrlRequestHandler {
private String targetUrlParameter = DEFAULT_TARGET_PARAMETER; private String targetUrlParameter = DEFAULT_TARGET_PARAMETER;
private String defaultTargetUrl = "/"; private String defaultTargetUrl = "/";
private boolean alwaysUseDefaultTargetUrl = false; private boolean alwaysUseDefaultTargetUrl = false;
private boolean useRelativeContext = false;
private boolean useReferer = false; private boolean useReferer = false;
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
protected AbstractAuthenticationTargetUrlRequestHandler() { protected AbstractAuthenticationTargetUrlRequestHandler() {
} }
@ -60,7 +61,7 @@ public abstract class AbstractAuthenticationTargetUrlRequestHandler {
throws IOException, ServletException { throws IOException, ServletException {
String targetUrl = determineTargetUrl(request, response); String targetUrl = determineTargetUrl(request, response);
RedirectUtils.sendRedirect(request, response, targetUrl, useRelativeContext); redirectStrategy.sendRedirect(request, response, targetUrl);
} }
private String determineTargetUrl(HttpServletRequest request, HttpServletResponse response) { private String determineTargetUrl(HttpServletRequest request, HttpServletResponse response) {
@ -149,15 +150,14 @@ public abstract class AbstractAuthenticationTargetUrlRequestHandler {
} }
/** /**
* If <tt>true</tt>, causes any redirection URLs to be calculated minus the protocol * Allows overriding of the behaviour when redirecting to a target URL.
* and context path (defaults to <tt>false</tt>).
*/ */
public void setUseRelativeContext(boolean useRelativeContext) { public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
this.useRelativeContext = useRelativeContext; this.redirectStrategy = redirectStrategy;
} }
protected boolean isUseRelativeContext() { protected RedirectStrategy getRedirectStrategy() {
return useRelativeContext; return redirectStrategy;
} }
/** /**

View File

@ -9,7 +9,6 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.util.RedirectUtils;
import org.springframework.security.web.util.UrlUtils; import org.springframework.security.web.util.UrlUtils;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -35,7 +34,7 @@ public class ExceptionMappingAuthenticationFailureHandler extends SimpleUrlAuthe
String url = failureUrlMap.get(exception.getClass().getName()); String url = failureUrlMap.get(exception.getClass().getName());
if (url != null) { if (url != null) {
RedirectUtils.sendRedirect(request, response, url, isUseRelativeContext()); getRedirectStrategy().sendRedirect(request, response, url);
} else { } else {
super.onAuthenticationFailure(request, response, exception); super.onAuthenticationFailure(request, response, exception);
} }

View File

@ -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 java.io.IOException;
import javax.servlet.RequestDispatcher; import javax.servlet.RequestDispatcher;
@ -42,6 +24,23 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; 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 * Used by the {@link ExceptionTranslationFilter} to commence a form login
* authentication via the {@link UsernamePasswordAuthenticationProcessingFilter}. This object * authentication via the {@link UsernamePasswordAuthenticationProcessingFilter}. This object
@ -80,6 +79,8 @@ public class LoginUrlAuthenticationEntryPoint implements AuthenticationEntryPoin
private boolean useForward = false; private boolean useForward = false;
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
//~ Methods ======================================================================================================== //~ Methods ========================================================================================================
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {
@ -117,6 +118,8 @@ public class LoginUrlAuthenticationEntryPoint implements AuthenticationEntryPoin
if (useForward) { if (useForward) {
if (forceHttps && "http".equals(request.getScheme())) { 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); 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, 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) protected String buildHttpsRedirectUrlForRequest(HttpServletRequest request)
throws IOException, ServletException { throws IOException, ServletException {

View File

@ -13,7 +13,6 @@ import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache; import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache; import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.SavedRequest; import org.springframework.security.web.savedrequest.SavedRequest;
import org.springframework.security.web.util.RedirectUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
@ -76,7 +75,7 @@ public class SavedRequestAwareAuthenticationSuccessHandler extends SimpleUrlAuth
// Use the SavedRequest URL // Use the SavedRequest URL
String targetUrl = savedRequest.getFullRequestUrl(); String targetUrl = savedRequest.getFullRequestUrl();
logger.debug("Redirecting to SavedRequest Url: " + targetUrl); logger.debug("Redirecting to SavedRequest Url: " + targetUrl);
RedirectUtils.sendRedirect(request, response, targetUrl, isUseRelativeContext()); getRedirectStrategy().sendRedirect(request, response, targetUrl);
} }
public void setRequestCache(RequestCache requestCache) { public void setRequestCache(RequestCache requestCache) {

View File

@ -7,7 +7,8 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException; 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.security.web.util.UrlUtils;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -27,7 +28,7 @@ import org.springframework.util.Assert;
public class SimpleUrlAuthenticationFailureHandler implements AuthenticationFailureHandler { public class SimpleUrlAuthenticationFailureHandler implements AuthenticationFailureHandler {
private String defaultFailureUrl; private String defaultFailureUrl;
private boolean forwardToDestination = false; private boolean forwardToDestination = false;
private boolean useRelativeContext = false; private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
public SimpleUrlAuthenticationFailureHandler() { public SimpleUrlAuthenticationFailureHandler() {
} }
@ -44,7 +45,7 @@ public class SimpleUrlAuthenticationFailureHandler implements AuthenticationFail
if (forwardToDestination) { if (forwardToDestination) {
request.getRequestDispatcher(defaultFailureUrl).forward(request, response); request.getRequestDispatcher(defaultFailureUrl).forward(request, response);
} else { } else {
RedirectUtils.sendRedirect(request, response, defaultFailureUrl, useRelativeContext); redirectStrategy.sendRedirect(request, response, defaultFailureUrl);
} }
} }
} }
@ -71,16 +72,14 @@ public class SimpleUrlAuthenticationFailureHandler implements AuthenticationFail
this.forwardToDestination = forwardToDestination; this.forwardToDestination = forwardToDestination;
} }
protected boolean isUseRelativeContext() {
return useRelativeContext;
}
/** /**
* If true, causes any redirection URLs to be calculated minus the protocol * Allows overriding of the behaviour when redirecting to a target URL.
* and context path (defaults to false).
*/ */
public void setUseRelativeContext(boolean useRelativeContext) { public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
this.useRelativeContext = useRelativeContext; this.redirectStrategy = redirectStrategy;
} }
protected RedirectStrategy getRedirectStrategy() {
return redirectStrategy;
}
} }

View File

@ -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));
}
}

View File

@ -43,6 +43,7 @@ import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.util.FieldUtils; 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.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.switchuser.SwitchUserAuthorityChanger; import org.springframework.security.web.authentication.switchuser.SwitchUserAuthorityChanger;
import org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority; import org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority;
@ -306,7 +307,9 @@ public class SwitchUserProcessingFilterTests {
filter.setSwitchUserUrl("/j_spring_security_switch_user"); filter.setSwitchUserUrl("/j_spring_security_switch_user");
SimpleUrlAuthenticationSuccessHandler switchSuccessHandler = SimpleUrlAuthenticationSuccessHandler switchSuccessHandler =
new SimpleUrlAuthenticationSuccessHandler("/someOtherUrl"); new SimpleUrlAuthenticationSuccessHandler("/someOtherUrl");
switchSuccessHandler.setUseRelativeContext(true); DefaultRedirectStrategy contextRelativeRedirector = new DefaultRedirectStrategy();
contextRelativeRedirector.setUseRelativeContext(true);
switchSuccessHandler.setRedirectStrategy(contextRelativeRedirector);
filter.setSuccessHandler(switchSuccessHandler); filter.setSuccessHandler(switchSuccessHandler);
filter.setUserDetailsService(new MockUserDetailsService()); filter.setUserDetailsService(new MockUserDetailsService());