diff --git a/core/src/main/java/org/springframework/security/ui/AbstractAuthenticationTargetUrlRequestHandler.java b/core/src/main/java/org/springframework/security/ui/AbstractAuthenticationTargetUrlRequestHandler.java new file mode 100644 index 0000000000..0617440028 --- /dev/null +++ b/core/src/main/java/org/springframework/security/ui/AbstractAuthenticationTargetUrlRequestHandler.java @@ -0,0 +1,169 @@ +package org.springframework.security.ui; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; + +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.security.Authentication; +import org.springframework.security.ui.logout.LogoutHandler; +import org.springframework.security.util.RedirectUtils; +import org.springframework.security.util.UrlUtils; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * Base class containing the logic used by strategies which handle redirection to a URL and + * are passed an Authentication object as part of the contract. + * See {@link AuthenticationSuccessHandler} and {@link LogoutHandler}, for example. + *
+ * Uses the following logic sequence to determine how it should handle the forward/redirect + *
/.
+ * Alternatively, inclusion of a scheme name (such as "http://" or "https://") as the prefix will denote a
+ * fully-qualified URL and this is also supported.
+ *
+ * @param defaultTargetUrl
+ */
+ public void setDefaultTargetUrl(String defaultTargetUrl) {
+ Assert.isTrue(UrlUtils.isValidRedirectUrl(defaultTargetUrl),
+ "defaultTarget must start with '/' or with 'http(s)'");
+ this.defaultTargetUrl = defaultTargetUrl;
+ }
+
+ /**
+ * If true, will always redirect to the value of defaultTargetUrl
+ * (defaults to false).
+ */
+ public void setAlwaysUseDefaultTargetUrl(boolean alwaysUseDefaultTargetUrl) {
+ this.alwaysUseDefaultTargetUrl = alwaysUseDefaultTargetUrl;
+ }
+
+ protected boolean isAlwaysUseDefaultTargetUrl() {
+ return alwaysUseDefaultTargetUrl;
+ }
+
+ /**
+ * The current request will be checked for this parameter before and the value used as the target URL if resent.
+ *
+ * @param targetUrlParameter the name of the parameter containing the encoded target URL. Defaults
+ * to "redirect".
+ */
+ public void setTargetUrlParameter(String targetUrlParameter) {
+ Assert.hasText("targetUrlParameter canot be null or empty");
+ this.targetUrlParameter = targetUrlParameter;
+ }
+
+ protected String getTargetUrlParameter() {
+ return targetUrlParameter;
+ }
+
+ /**
+ * 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;
+ }
+
+ protected boolean isUseRelativeContext() {
+ return useRelativeContext;
+ }
+
+ /**
+ * If set to true the Referer header will be used (if available). Defaults to false.
+ */
+ public void setUseReferer(boolean useReferer) {
+ this.useReferer = useReferer;
+ }
+}
diff --git a/core/src/main/java/org/springframework/security/ui/LogoutSuccessHandler.java b/core/src/main/java/org/springframework/security/ui/LogoutSuccessHandler.java
new file mode 100644
index 0000000000..6a6420bb6f
--- /dev/null
+++ b/core/src/main/java/org/springframework/security/ui/LogoutSuccessHandler.java
@@ -0,0 +1,29 @@
+package org.springframework.security.ui;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.Authentication;
+import org.springframework.security.ui.logout.LogoutFilter;
+
+/**
+ * Strategy that is called after a successful logout by the {@link LogoutFilter}, to handle redirection or
+ * forwarding to the appropriate destination.
+ * + * Note that the interface is almost the same as {@link LogoutHandler} but may raise an exception. + * LogoutHandler implementations expect to be invoked to perform necessary cleanup, so should not throw + * exceptions. + * + * @author Luke Taylor + * @version $Id$ + * @since 2.5 + */ +public interface LogoutSuccessHandler { + + void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) + throws IOException, ServletException; + +} diff --git a/core/src/main/java/org/springframework/security/ui/SavedRequestAwareAuthenticationSuccessHandler.java b/core/src/main/java/org/springframework/security/ui/SavedRequestAwareAuthenticationSuccessHandler.java index bb5c10d567..f1c8a9f8a3 100644 --- a/core/src/main/java/org/springframework/security/ui/SavedRequestAwareAuthenticationSuccessHandler.java +++ b/core/src/main/java/org/springframework/security/ui/SavedRequestAwareAuthenticationSuccessHandler.java @@ -14,9 +14,13 @@ import org.springframework.security.wrapper.SavedRequestAwareWrapper; import org.springframework.util.StringUtils; /** - * Decides on the redirect destination following a successful authentication, based on the following - * configuration options: - * + * An authentication success strategy which can make use of the {@link SavedRequest} which may have been stored in + * the session by the {@link ExceptionTranslationFilter}. When such a request is intercepted and requires authentication, + * the request data is stored to record the original destination before the authentication process commenced, and to + * allow the request to be reconstructed when a redirect to the same URL occurs. This class is responsible for + * performing the redirect to the original URL if appropriate. + *
+ * Following a successful authentication, it decides on the redirect destination, based on the following scenarios: *
* If the forwardToDestination parameter is set, a RequestDispatcher.forward call will be made to
- * the destination instead of
+ * the destination instead of a redirect.
*
* @author Luke Taylor
* @version $Id$
diff --git a/core/src/main/java/org/springframework/security/ui/SimpleUrlAuthenticationSuccessHandler.java b/core/src/main/java/org/springframework/security/ui/SimpleUrlAuthenticationSuccessHandler.java
index 201444e310..ec5b37923f 100644
--- a/core/src/main/java/org/springframework/security/ui/SimpleUrlAuthenticationSuccessHandler.java
+++ b/core/src/main/java/org/springframework/security/ui/SimpleUrlAuthenticationSuccessHandler.java
@@ -1,29 +1,14 @@
package org.springframework.security.ui;
import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
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.security.Authentication;
-import org.springframework.security.util.RedirectUtils;
-import org.springframework.security.util.UrlUtils;
-import org.springframework.util.Assert;
-import org.springframework.util.StringUtils;
-public class SimpleUrlAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
-
- public static String DEFAULT_TARGET_PARAMETER = "spring-security-redirect";
- protected final Log logger = LogFactory.getLog(this.getClass());
- protected String targetUrlParameter = DEFAULT_TARGET_PARAMETER;
- protected String defaultTargetUrl = "/";
- protected boolean alwaysUseDefaultTargetUrl = false;
- protected boolean useRelativeContext = false;
+public class SimpleUrlAuthenticationSuccessHandler extends AbstractAuthenticationTargetUrlRequestHandler implements AuthenticationSuccessHandler {
public SimpleUrlAuthenticationSuccessHandler() {
}
@@ -35,90 +20,6 @@ public class SimpleUrlAuthenticationSuccessHandler implements AuthenticationSucc
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
- if (isAlwaysUseDefaultTargetUrl()) {
- RedirectUtils.sendRedirect(request, response, getDefaultTargetUrl(), useRelativeContext);
- return;
- }
-
- // Check for the parameter and use that if available
- String targetUrl = request.getParameter(targetUrlParameter);
-
- if (StringUtils.hasText(targetUrl)) {
- try {
- targetUrl = URLDecoder.decode(targetUrl, "UTF-8");
- } catch (UnsupportedEncodingException e) {
- throw new IllegalStateException("UTF-8 not supported. Shouldn't be possible");
- }
-
- logger.debug("Found targetUrlParameter in request. Redirecting to: " + targetUrl);
-
- RedirectUtils.sendRedirect(request, response, targetUrl, useRelativeContext);
-
- return;
- }
-
- if (targetUrl == null) {
- targetUrl = getDefaultTargetUrl();
- logger.debug("Redirecting to default Url: " + targetUrl);
- }
-
- RedirectUtils.sendRedirect(request, response, targetUrl, useRelativeContext);
- }
-
- /**
- * Supplies the default target Url that will be used if no saved request is found or the
- * alwaysUseDefaultTargetUrl property is set to true. If not set, defaults to /.
- *
- * @return the defaultTargetUrl property
- */
- protected String getDefaultTargetUrl() {
- return defaultTargetUrl;
- }
-
- /**
- * Supplies the default target Url that will be used if no saved request is found in the session, or the
- * alwaysUseDefaultTargetUrl property is set to true. If not set, defaults to /. It
- * will be treated as relative to the web-app's context path, and should include the leading /.
- * Alternatively, inclusion of a scheme name (such as "http://" or "https://") as the prefix will denote a
- * fully-qualified URL and this is also supported.
- *
- * @param defaultTargetUrl
- */
- public void setDefaultTargetUrl(String defaultTargetUrl) {
- Assert.isTrue(UrlUtils.isValidRedirectUrl(defaultTargetUrl),
- "defaultTarget must start with '/' or with 'http(s)'");
- this.defaultTargetUrl = defaultTargetUrl;
- }
-
- /**
- * If true, will always redirect to the value of defaultTargetUrl
- * (defaults to false).
- */
- public void setAlwaysUseDefaultTargetUrl(boolean alwaysUseDefaultTargetUrl) {
- this.alwaysUseDefaultTargetUrl = alwaysUseDefaultTargetUrl;
- }
-
- protected boolean isAlwaysUseDefaultTargetUrl() {
- return alwaysUseDefaultTargetUrl;
- }
-
- /**
- * Before checking the SavedRequest, the current request will be checked for this parameter
- * and the value used as the target URL if resent.
- *
- * @param targetUrlParameter the name of the parameter containing the encoded target URL. Defaults
- * to "redirect".
- */
- public void setTargetUrlParameter(String targetUrlParameter) {
- Assert.hasText("targetUrlParameter canot be null or empty");
- this.targetUrlParameter = targetUrlParameter;
- }
-
- /**
- * 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;
+ handle(request, response, authentication);
}
}
diff --git a/core/src/main/java/org/springframework/security/ui/logout/LogoutFilter.java b/core/src/main/java/org/springframework/security/ui/logout/LogoutFilter.java
index 3e5c68ec09..9f5ecbc275 100644
--- a/core/src/main/java/org/springframework/security/ui/logout/LogoutFilter.java
+++ b/core/src/main/java/org/springframework/security/ui/logout/LogoutFilter.java
@@ -16,6 +16,8 @@
package org.springframework.security.ui.logout;
import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
@@ -23,11 +25,11 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.Authentication;
-import org.springframework.security.ui.SpringSecurityFilter;
-import org.springframework.security.ui.FilterChainOrder;
-import org.springframework.security.util.RedirectUtils;
-import org.springframework.security.util.UrlUtils;
import org.springframework.security.context.SecurityContextHolder;
+import org.springframework.security.ui.FilterChainOrder;
+import org.springframework.security.ui.LogoutSuccessHandler;
+import org.springframework.security.ui.SpringSecurityFilter;
+import org.springframework.security.util.UrlUtils;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -37,11 +39,10 @@ import org.springframework.util.StringUtils;
* Polls a series of {@link LogoutHandler}s. The handlers should be specified in the order they are required.
* Generally you will want to call logout handlers TokenBasedRememberMeServices and
* SecurityContextLogoutHandler (in that order).
- *
- * After logout, the URL specified by {@link #logoutSuccessUrl} will be shown. - *
- * + * After logout, a redirect will be performed to the URL determined by either the configured + * LogoutSuccessHandler or the logoutSuccessUrl, depending on which constructor was used. + * * @author Ben Alex * @version $Id$ */ @@ -50,17 +51,32 @@ public class LogoutFilter extends SpringSecurityFilter { //~ Instance fields ================================================================================================ private String filterProcessesUrl = "/j_spring_security_logout"; - private String logoutSuccessUrl; - private LogoutHandler[] handlers; - private boolean useRelativeContext; + private List
- * By default it will check for a logoutSuccessUrl parameter in
- * the request and use this. If that isn't present it will use the configured logoutSuccessUrl. If this
- * hasn't been set it will check the Referer header and use the URL from there.
- *
- */
- protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response) {
- String targetUrl = request.getParameter("logoutSuccessUrl");
-
- if(!StringUtils.hasLength(targetUrl)) {
- targetUrl = getLogoutSuccessUrl();
- }
-
- if (!StringUtils.hasLength(targetUrl)) {
- targetUrl = request.getHeader("Referer");
- }
-
- if (!StringUtils.hasLength(targetUrl)) {
- targetUrl = "/";
- }
-
- return targetUrl;
- }
-
- /**
- * Allow subclasses to modify the redirection message.
- *
- * @param request the request
- * @param response the response
- * @param url the URL to redirect to
- *
- * @throws IOException in the event of any failure
- */
- protected void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url)
- throws IOException {
-
- RedirectUtils.sendRedirect(request, response, url, useRelativeContext);
- }
-
public void setFilterProcessesUrl(String filterProcessesUrl) {
- Assert.hasText(filterProcessesUrl, "FilterProcessesUrl required");
- Assert.isTrue(UrlUtils.isValidRedirectUrl(filterProcessesUrl), filterProcessesUrl + " isn't a valid redirect URL");
+ Assert.isTrue(UrlUtils.isValidRedirectUrl(filterProcessesUrl), filterProcessesUrl + " isn't a valid value for" +
+ " 'filterProcessesUrl'");
this.filterProcessesUrl = filterProcessesUrl;
}
- protected String getLogoutSuccessUrl() {
- return logoutSuccessUrl;
- }
-
protected String getFilterProcessesUrl() {
return filterProcessesUrl;
}
- public void setUseRelativeContext(boolean useRelativeContext) {
- this.useRelativeContext = useRelativeContext;
- }
-
public int getOrder() {
return FilterChainOrder.LOGOUT_FILTER;
}
diff --git a/core/src/main/java/org/springframework/security/ui/logout/SimpleUrlLogoutSuccessHandler.java b/core/src/main/java/org/springframework/security/ui/logout/SimpleUrlLogoutSuccessHandler.java
new file mode 100644
index 0000000000..22da713e35
--- /dev/null
+++ b/core/src/main/java/org/springframework/security/ui/logout/SimpleUrlLogoutSuccessHandler.java
@@ -0,0 +1,29 @@
+package org.springframework.security.ui.logout;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.Authentication;
+import org.springframework.security.ui.AbstractAuthenticationTargetUrlRequestHandler;
+import org.springframework.security.ui.LogoutSuccessHandler;
+
+/**
+ * Handles the navigation on logout by delegating to the {@link AbstractAuthenticationTargetUrlRequestHandler}
+ * base class logic.
+ *
+ * @author Luke Taylor
+ * @version $Id$
+ * @since 2.5
+ */
+public class SimpleUrlLogoutSuccessHandler extends AbstractAuthenticationTargetUrlRequestHandler
+ implements LogoutSuccessHandler {
+
+ public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
+ throws IOException, ServletException {
+ super.handle(request, response, authentication);
+ }
+
+}
diff --git a/core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java b/core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java
index 3592fc480f..e5d43cefd7 100644
--- a/core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java
+++ b/core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java
@@ -395,6 +395,7 @@ public class HttpSecurityBeanDefinitionParserTests {
}
@Test
+ @SuppressWarnings("unchecked")
public void rememberMeServiceWorksWithExternalServicesImpl() throws Exception {
setContext(
"