diff --git a/config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java b/config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java
index 01731fed9b..77017338fb 100644
--- a/config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java
+++ b/config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java
@@ -46,6 +46,7 @@ import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
import org.springframework.security.web.session.ConcurrentSessionFilter;
import org.springframework.security.web.session.SessionManagementFilter;
+import org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
@@ -270,7 +271,7 @@ class HttpConfigurationBuilder {
}
if (StringUtils.hasText(invalidSessionUrl)) {
- sessionMgmtFilter.addPropertyValue("invalidSessionUrl", invalidSessionUrl);
+ sessionMgmtFilter.addPropertyValue("invalidSessionStrategy", new SimpleRedirectInvalidSessionStrategy(invalidSessionUrl));
}
sessionMgmtFilter.addPropertyReference("sessionAuthenticationStrategy", sessionAuthStratRef);
diff --git a/config/src/test/groovy/org/springframework/security/config/http/SessionManagementConfigTests.groovy b/config/src/test/groovy/org/springframework/security/config/http/SessionManagementConfigTests.groovy
index a1019f54e6..9a910a5921 100644
--- a/config/src/test/groovy/org/springframework/security/config/http/SessionManagementConfigTests.groovy
+++ b/config/src/test/groovy/org/springframework/security/config/http/SessionManagementConfigTests.groovy
@@ -124,7 +124,7 @@ class SessionManagementConfigTests extends AbstractHttpConfigTests {
Object sessionRegistryFromFormLoginFilter = FieldUtils.getFieldValue(
getFilter(UsernamePasswordAuthenticationFilter.class),"sessionStrategy.sessionRegistry");
Object sessionRegistryFromMgmtFilter = FieldUtils.getFieldValue(
- getFilter(SessionManagementFilter.class),"sessionStrategy.sessionRegistry");
+ getFilter(SessionManagementFilter.class),"sessionAuthenticationStrategy.sessionRegistry");
assertSame(sessionRegistry, sessionRegistryFromConcurrencyFilter);
assertSame(sessionRegistry, sessionRegistryFromMgmtFilter);
@@ -183,7 +183,7 @@ class SessionManagementConfigTests extends AbstractHttpConfigTests {
expect:
filter instanceof SessionManagementFilter
- filter.invalidSessionUrl == '/timeoutUrl'
+ filter.invalidSessionStrategy.destinationUrl == '/timeoutUrl'
}
}
diff --git a/docs/manual/src/docbook/appendix-namespace.xml b/docs/manual/src/docbook/appendix-namespace.xml
index e6ae087d05..ea5a981cb8 100644
--- a/docs/manual/src/docbook/appendix-namespace.xml
+++ b/docs/manual/src/docbook/appendix-namespace.xml
@@ -404,6 +404,14 @@
configured DefaultSessionAuthenticationStrategy. See the
Javadoc for this class for more details.
+
+ invalid-session-url
+ Setting this attribute will inject the SessionManagementFilter
+ with a SimpleRedirectInvalidSessionStrategy configured with
+ the attribute value. When an invalid session ID is submitted, the strategy will be invoked,
+ redirecting to the configured URL.
+
+ The <concurrency-control> Element
diff --git a/docs/manual/src/docbook/session-mgmt.xml b/docs/manual/src/docbook/session-mgmt.xml
index 4e305346e7..785f955dbe 100644
--- a/docs/manual/src/docbook/session-mgmt.xml
+++ b/docs/manual/src/docbook/session-mgmt.xml
@@ -28,10 +28,12 @@
invoke the configured
SessionAuthenticationStrategy.
If the user is not currently authenticated, the filter will check whether an invalid
- session ID has been requested (because of a timeout, for example) and will redirect to
- the configured invalidSessionUrl if set. The easiest way to configure
- this is through the namespace, as described
- earlier.
+ session ID has been requested (because of a timeout, for example) and will invoke the configured
+ InvalidSessionStrategy, if one is set. The most common behaviour
+ is just to redirect to a fixed URL and this is encapsulated in the standard implementation
+ SimpleRedirectInvalidSessionStrategy. The latter is also used
+ when configuring an invalid session URL through the namespace,
+ as described earlier.
SessionAuthenticationStrategy
diff --git a/web/src/main/java/org/springframework/security/web/session/InvalidSessionStrategy.java b/web/src/main/java/org/springframework/security/web/session/InvalidSessionStrategy.java
new file mode 100644
index 0000000000..e9eaed5882
--- /dev/null
+++ b/web/src/main/java/org/springframework/security/web/session/InvalidSessionStrategy.java
@@ -0,0 +1,18 @@
+package org.springframework.security.web.session;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * Determines the behaviour of the {@code SessionManagementFilter} when an invalid session Id is submitted and
+ * detected in the {@code SessionManagementFilter}.
+ *
+ * @author Luke Taylor
+ */
+public interface InvalidSessionStrategy {
+
+ void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException;
+
+}
diff --git a/web/src/main/java/org/springframework/security/web/session/SessionManagementFilter.java b/web/src/main/java/org/springframework/security/web/session/SessionManagementFilter.java
index 55da4d9c2c..d6dd36312c 100644
--- a/web/src/main/java/org/springframework/security/web/session/SessionManagementFilter.java
+++ b/web/src/main/java/org/springframework/security/web/session/SessionManagementFilter.java
@@ -13,8 +13,6 @@ import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.security.web.DefaultRedirectStrategy;
-import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.session.SessionAuthenticationException;
@@ -41,11 +39,10 @@ public class SessionManagementFilter extends GenericFilterBean {
//~ Instance fields ================================================================================================
private final SecurityContextRepository securityContextRepository;
- private SessionAuthenticationStrategy sessionStrategy;
+ private SessionAuthenticationStrategy sessionAuthenticationStrategy;
private final AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
- private String invalidSessionUrl;
+ private InvalidSessionStrategy invalidSessionStrategy = null;
private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
- private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
public SessionManagementFilter(SecurityContextRepository securityContextRepository) {
this(securityContextRepository, new SessionFixationProtectionStrategy());
@@ -55,7 +52,7 @@ public class SessionManagementFilter extends GenericFilterBean {
Assert.notNull(securityContextRepository, "SecurityContextRepository cannot be null");
Assert.notNull(sessionStrategy, "SessionAuthenticationStrategy cannot be null");
this.securityContextRepository = securityContextRepository;
- this.sessionStrategy = sessionStrategy;
+ this.sessionAuthenticationStrategy = sessionStrategy;
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
@@ -76,7 +73,7 @@ public class SessionManagementFilter extends GenericFilterBean {
if (authentication != null && !authenticationTrustResolver.isAnonymous(authentication)) {
// The user has been authenticated during the current request, so call the session strategy
try {
- sessionStrategy.onAuthentication(authentication, request, response);
+ sessionAuthenticationStrategy.onAuthentication(authentication, request, response);
} catch (SessionAuthenticationException e) {
// The session strategy can reject the authentication
logger.debug("SessionAuthenticationStrategy rejected the authentication object", e);
@@ -93,11 +90,8 @@ public class SessionManagementFilter extends GenericFilterBean {
if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) {
logger.debug("Requested session ID" + request.getRequestedSessionId() + " is invalid.");
- if (invalidSessionUrl != null) {
- logger.debug("Starting new session (if required) and redirecting to '" + invalidSessionUrl + "'");
- request.getSession();
- redirectStrategy.sendRedirect(request, response, invalidSessionUrl);
-
+ if (invalidSessionStrategy != null) {
+ invalidSessionStrategy.onInvalidSessionDetected(request, response);
return;
}
}
@@ -115,19 +109,19 @@ public class SessionManagementFilter extends GenericFilterBean {
* @deprecated Use constructor injection
*/
@Deprecated
- public void setSessionAuthenticationStrategy(SessionAuthenticationStrategy sessionStrategy) {
- Assert.notNull(sessionStrategy, "authenticatedSessionStratedy must not be null");
- this.sessionStrategy = sessionStrategy;
+ public void setSessionAuthenticationStrategy(SessionAuthenticationStrategy sessionAuthenticationStrategy) {
+ Assert.notNull(sessionAuthenticationStrategy, "authenticatedSessionStratedy must not be null");
+ this.sessionAuthenticationStrategy = sessionAuthenticationStrategy;
}
/**
- * Sets the URL to which the response should be redirected if the user agent requests an invalid session Id.
- * If the property is not set, no action will be taken.
+ * Sets the strategy which will be invoked instead of allowing the filter chain to prceed, if the user agent
+ * requests an invalid session Id. If the property is not set, no action will be taken.
*
- * @param invalidSessionUrl
+ * @param invalidSessionStrategy the strategy to invoke. Typically a {@link SimpleRedirectInvalidSessionStrategy}.
*/
- public void setInvalidSessionUrl(String invalidSessionUrl) {
- this.invalidSessionUrl = invalidSessionUrl;
+ public void setInvalidSessionStrategy(InvalidSessionStrategy invalidSessionStrategy) {
+ this.invalidSessionStrategy = invalidSessionStrategy;
}
/**
@@ -140,8 +134,4 @@ public class SessionManagementFilter extends GenericFilterBean {
Assert.notNull(failureHandler, "failureHandler cannot be null");
this.failureHandler = failureHandler;
}
-
- public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
- this.redirectStrategy = redirectStrategy;
- }
}
diff --git a/web/src/main/java/org/springframework/security/web/session/SimpleRedirectInvalidSessionStrategy.java b/web/src/main/java/org/springframework/security/web/session/SimpleRedirectInvalidSessionStrategy.java
new file mode 100644
index 0000000000..16e5e2d934
--- /dev/null
+++ b/web/src/main/java/org/springframework/security/web/session/SimpleRedirectInvalidSessionStrategy.java
@@ -0,0 +1,48 @@
+package org.springframework.security.web.session;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+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 javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * Performs a redirect to a fixed URL when an invalid requested session is detected by the {@code SessionManagementFilter}.
+ *
+ * @author Luke Taylor
+ */
+public final class SimpleRedirectInvalidSessionStrategy implements InvalidSessionStrategy {
+ private final Log logger = LogFactory.getLog(getClass());
+ private final String destinationUrl;
+ private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
+ private boolean createNewSession = true;
+
+ public SimpleRedirectInvalidSessionStrategy(String invalidSessionUrl) {
+ Assert.isTrue(UrlUtils.isValidRedirectUrl(invalidSessionUrl), "url must start with '/' or with 'http(s)'");
+ this.destinationUrl = invalidSessionUrl;
+ }
+
+ public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ logger.debug("Starting new session (if required) and redirecting to '" + destinationUrl + "'");
+ if (createNewSession) {
+ request.getSession();
+ }
+ redirectStrategy.sendRedirect(request, response, destinationUrl);
+ }
+
+ /**
+ * Determines whether a new session should be created before redirecting (to avoid possible looping issues where
+ * the same session ID is sent with the redirected request). Alternatively, ensure that the configured URL
+ * does not pass through the {@code SessionManagementFilter}.
+ *
+ * @param createNewSession defaults to {@code true}.
+ */
+ public void setCreateNewSession(boolean createNewSession) {
+ this.createNewSession = createNewSession;
+ }
+}
diff --git a/web/src/test/java/org/springframework/security/web/session/SessionManagementFilterTests.java b/web/src/test/java/org/springframework/security/web/session/SessionManagementFilterTests.java
index d1e7c5943c..ae2fa2c5fe 100644
--- a/web/src/test/java/org/springframework/security/web/session/SessionManagementFilterTests.java
+++ b/web/src/test/java/org/springframework/security/web/session/SessionManagementFilterTests.java
@@ -121,7 +121,6 @@ public class SessionManagementFilterTests {
SessionAuthenticationStrategy strategy = mock(SessionAuthenticationStrategy.class);
SessionManagementFilter filter = new SessionManagementFilter(repo);
filter.setSessionAuthenticationStrategy(strategy);
- filter.setRedirectStrategy(new DefaultRedirectStrategy());
MockHttpServletRequest request = new MockHttpServletRequest();
request.setRequestedSessionId("xxx");
request.setRequestedSessionIdValid(false);
@@ -134,7 +133,9 @@ public class SessionManagementFilterTests {
request = new MockHttpServletRequest();
request.setRequestedSessionId("xxx");
request.setRequestedSessionIdValid(false);
- filter.setInvalidSessionUrl("/timedOut");
+ SimpleRedirectInvalidSessionStrategy iss = new SimpleRedirectInvalidSessionStrategy("/timedOut");
+ iss.setCreateNewSession(true);
+ filter.setInvalidSessionStrategy(iss);
FilterChain fc = mock(FilterChain.class);
filter.doFilter(request, response, fc);
verifyZeroInteractions(fc);