From 3d9ea49d19c91a6b9938fc9687e73b3923f6a388 Mon Sep 17 00:00:00 2001 From: Luke Taylor Date: Fri, 26 Oct 2007 23:32:40 +0000 Subject: [PATCH] SEC-585: Made expiredUrl optional. Also implemented Ordered interface for use in namespace configuration. --- .../concurrent/ConcurrentSessionFilter.java | 70 ++++++++----------- .../ConcurrentSessionFilterTests.java | 37 +++++----- 2 files changed, 49 insertions(+), 58 deletions(-) diff --git a/core/src/main/java/org/springframework/security/concurrent/ConcurrentSessionFilter.java b/core/src/main/java/org/springframework/security/concurrent/ConcurrentSessionFilter.java index d9106f2019..062079d97f 100644 --- a/core/src/main/java/org/springframework/security/concurrent/ConcurrentSessionFilter.java +++ b/core/src/main/java/org/springframework/security/concurrent/ConcurrentSessionFilter.java @@ -16,36 +16,34 @@ package org.springframework.security.concurrent; import org.springframework.beans.factory.InitializingBean; - +import org.springframework.security.ui.FilterChainOrderUtils; +import org.springframework.security.ui.SpringSecurityFilter; import org.springframework.util.Assert; -import java.io.IOException; - -import javax.servlet.Filter; import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; +import java.io.IOException; /** - * Filter required by concurrent session handling package.

This filter performs two functions. First, it calls - * {@link org.springframework.security.concurrent.SessionRegistry#refreshLastRequest(String)} for each request. That way, - * registered sessions always have a correct "last update" date/time. Second, it retrieves {@link - * org.springframework.security.concurrent.SessionInformation} from the SessionRegistry for each request and checks - * if the session has been marked as expired. If it has been marked as expired, the session is invalidated. The - * invalidation of the session will also cause the request to redirect to the URL specified, and a {@link - * org.springframework.security.ui.session.HttpSessionDestroyedEvent} to be published via the {@link - * org.springframework.security.ui.session.HttpSessionEventPublisher} registered in web.xml.

+ * Filter required by concurrent session handling package. + *

This filter performs two functions. First, it calls + * {@link org.springframework.security.concurrent.SessionRegistry#refreshLastRequest(String)} for each request. + * That way, registered sessions always have a correct "last update" date/time. Second, it retrieves + * {@link org.springframework.security.concurrent.SessionInformation} from the SessionRegistry + * for each request and checks if the session has been marked as expired. + * If it has been marked as expired, the session is invalidated. The invalidation of the session will also cause the + * request to redirect to the URL specified, and a + * {@link org.springframework.security.ui.session.HttpSessionDestroyedEvent} to be published via the + * {@link org.springframework.security.ui.session.HttpSessionEventPublisher} registered in web.xml.

* * @author Ben Alex * @version $Id$ */ -public class ConcurrentSessionFilter implements Filter, InitializingBean { +public class ConcurrentSessionFilter extends SpringSecurityFilter implements InitializingBean { //~ Instance fields ================================================================================================ private SessionRegistry sessionRegistry; @@ -55,23 +53,12 @@ public class ConcurrentSessionFilter implements Filter, InitializingBean { public void afterPropertiesSet() throws Exception { Assert.notNull(sessionRegistry, "SessionRegistry required"); - Assert.hasText(expiredUrl, "ExpiredUrl required"); } - /** - * Does nothing. We use IoC container lifecycle services instead. - */ - public void destroy() {} + public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + throws IOException, ServletException { - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) - throws IOException, ServletException { - Assert.isInstanceOf(HttpServletRequest.class, request, "Can only process HttpServletRequest"); - Assert.isInstanceOf(HttpServletResponse.class, response, "Can only process HttpServletResponse"); - - HttpServletRequest httpRequest = (HttpServletRequest) request; - HttpServletResponse httpResponse = (HttpServletResponse) response; - - HttpSession session = httpRequest.getSession(false); + HttpSession session = request.getSession(false); if (session != null) { SessionInformation info = sessionRegistry.getSessionInformation(session.getId()); @@ -81,8 +68,14 @@ public class ConcurrentSessionFilter implements Filter, InitializingBean { // Expired - abort processing session.invalidate(); - String targetUrl = httpRequest.getContextPath() + expiredUrl; - httpResponse.sendRedirect(httpResponse.encodeRedirectURL(targetUrl)); + if (expiredUrl != null) { + String targetUrl = request.getContextPath() + expiredUrl; + response.sendRedirect(response.encodeRedirectURL(targetUrl)); + } else { + response.getWriter().print("This session has been expired (possibly due to multiple concurrent " + + "logins being attempted as the same user)."); + response.flushBuffer(); + } return; } else { @@ -95,15 +88,6 @@ public class ConcurrentSessionFilter implements Filter, InitializingBean { chain.doFilter(request, response); } - /** - * Does nothing. We use IoC container lifecycle services instead. - * - * @param arg0 ignored - * - * @throws ServletException ignored - */ - public void init(FilterConfig arg0) throws ServletException {} - public void setExpiredUrl(String expiredUrl) { this.expiredUrl = expiredUrl; } @@ -111,4 +95,8 @@ public class ConcurrentSessionFilter implements Filter, InitializingBean { public void setSessionRegistry(SessionRegistry sessionRegistry) { this.sessionRegistry = sessionRegistry; } + + public int getOrder() { + return FilterChainOrderUtils.CONCURRENT_SESSION_FILTER_ORDER; + } } diff --git a/core/src/test/java/org/springframework/security/concurrent/ConcurrentSessionFilterTests.java b/core/src/test/java/org/springframework/security/concurrent/ConcurrentSessionFilterTests.java index bca3a21e66..39e9d7555d 100644 --- a/core/src/test/java/org/springframework/security/concurrent/ConcurrentSessionFilterTests.java +++ b/core/src/test/java/org/springframework/security/concurrent/ConcurrentSessionFilterTests.java @@ -40,10 +40,6 @@ import java.util.Date; public class ConcurrentSessionFilterTests extends TestCase { //~ Constructors =================================================================================================== - public ConcurrentSessionFilterTests() { - super(); - } - public ConcurrentSessionFilterTests(String arg0) { super(arg0); } @@ -58,10 +54,6 @@ public class ConcurrentSessionFilterTests extends TestCase { filter.destroy(); } - public static void main(String[] args) { - junit.textui.TestRunner.run(ConcurrentSessionFilterTests.class); - } - public void testDetectsExpiredSessions() throws Exception { // Setup our HTTP request MockHttpServletRequest request = new MockHttpServletRequest(); @@ -88,16 +80,27 @@ public class ConcurrentSessionFilterTests extends TestCase { assertEquals("/expired.jsp", response.getRedirectedUrl()); } - public void testDetectsMissingExpiredUrl() throws Exception { - ConcurrentSessionFilter filter = new ConcurrentSessionFilter(); - filter.setSessionRegistry(new SessionRegistryImpl()); + // As above, but with no expiredUrl set. + public void testReturnsExpectedMessageWhenNoExpiredUrlSet() throws Exception { + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpSession session = new MockHttpSession(); + request.setSession(session); - try { - filter.afterPropertiesSet(); - fail("Should have thrown IAE"); - } catch (IllegalArgumentException expected) { - assertTrue(true); - } + MockHttpServletResponse response = new MockHttpServletResponse(); + MockFilterConfig config = new MockFilterConfig(null, null); + + MockFilterChain chain = new MockFilterChain(false); + + ConcurrentSessionFilter filter = new ConcurrentSessionFilter(); + SessionRegistry registry = new SessionRegistryImpl(); + registry.registerNewSession(session.getId(), "principal"); + registry.getSessionInformation(session.getId()).expireNow(); + filter.setSessionRegistry(registry); + + executeFilterInContainerSimulator(config, filter, request, response, chain); + + assertEquals("This session has been expired (possibly due to multiple concurrent logins being " + + "attempted as the same user).", response.getContentAsString()); } public void testDetectsMissingSessionRegistry() throws Exception {