Refactored SessionRegistryImpl to remove servlet API deps and moved back into core, along with other concurrent authentication package classes.

This commit is contained in:
Luke Taylor 2009-04-21 06:05:14 +00:00
parent 75d5e8f5f2
commit cac2bce382
12 changed files with 80 additions and 114 deletions

View File

@ -11,8 +11,8 @@ import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext; import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.authentication.ProviderManager; import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.concurrent.ConcurrentSessionControllerImpl; import org.springframework.security.authentication.concurrent.ConcurrentSessionControllerImpl;
import org.springframework.security.authentication.concurrent.SessionRegistryImpl;
import org.springframework.security.web.concurrent.ConcurrentSessionFilter; import org.springframework.security.web.concurrent.ConcurrentSessionFilter;
import org.springframework.security.web.concurrent.SessionRegistryImpl;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.w3c.dom.Element; import org.w3c.dom.Element;

View File

@ -27,6 +27,7 @@ import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.concurrent.ConcurrentLoginException; import org.springframework.security.authentication.concurrent.ConcurrentLoginException;
import org.springframework.security.authentication.concurrent.ConcurrentSessionControllerImpl; import org.springframework.security.authentication.concurrent.ConcurrentSessionControllerImpl;
import org.springframework.security.authentication.concurrent.SessionRegistryImpl;
import org.springframework.security.config.util.InMemoryXmlApplicationContext; import org.springframework.security.config.util.InMemoryXmlApplicationContext;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.openid.OpenIDAuthenticationProcessingFilter; import org.springframework.security.openid.OpenIDAuthenticationProcessingFilter;
@ -51,7 +52,6 @@ import org.springframework.security.web.authentication.rememberme.PersistentToke
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices; import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;
import org.springframework.security.web.authentication.www.BasicProcessingFilter; import org.springframework.security.web.authentication.www.BasicProcessingFilter;
import org.springframework.security.web.concurrent.ConcurrentSessionFilter; import org.springframework.security.web.concurrent.ConcurrentSessionFilter;
import org.springframework.security.web.concurrent.SessionRegistryImpl;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository; import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextPersistenceFilter; import org.springframework.security.web.context.SecurityContextPersistenceFilter;
import org.springframework.security.web.expression.DefaultWebSecurityExpressionHandler; import org.springframework.security.web.expression.DefaultWebSecurityExpressionHandler;

View File

@ -7,11 +7,11 @@ import org.junit.Test;
import org.springframework.context.support.AbstractXmlApplicationContext; import org.springframework.context.support.AbstractXmlApplicationContext;
import org.springframework.security.authentication.concurrent.ConcurrentSessionController; import org.springframework.security.authentication.concurrent.ConcurrentSessionController;
import org.springframework.security.authentication.concurrent.ConcurrentSessionControllerImpl; import org.springframework.security.authentication.concurrent.ConcurrentSessionControllerImpl;
import org.springframework.security.authentication.concurrent.SessionRegistryImpl;
import org.springframework.security.config.util.InMemoryXmlApplicationContext; import org.springframework.security.config.util.InMemoryXmlApplicationContext;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
import org.springframework.security.util.FieldUtils; import org.springframework.security.util.FieldUtils;
import org.springframework.security.web.concurrent.SessionRegistryImpl;
/** /**
* *

View File

@ -1,34 +0,0 @@
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.authentication.concurrent;
import org.springframework.security.core.AuthenticationException;
/**
* Thrown by a <code>SessionRegistry</code> implementation if an attempt is made to create new session information
* for an existing sessionId. The user should firstly clear the existing session from the
* <code>ConcurrentSessionRegistry</code>.
*
* @author Ben Alex
*/
public class SessionAlreadyUsedException extends AuthenticationException {
//~ Constructors ===================================================================================================
public SessionAlreadyUsedException(String msg) {
super(msg);
}
}

View File

@ -67,11 +67,8 @@ public interface SessionRegistry {
* *
* @param sessionId to associate with the principal (should never be <code>null</code>) * @param sessionId to associate with the principal (should never be <code>null</code>)
* @param principal to associate with the session (should never be <code>null</code>) * @param principal to associate with the session (should never be <code>null</code>)
*
* @throws SessionAlreadyUsedException DOCUMENT ME!
*/ */
void registerNewSession(String sessionId, Object principal) void registerNewSession(String sessionId, Object principal);
throws SessionAlreadyUsedException;
/** /**
* Deletes all the session information being maintained for the specified <code>sessionId</code>. If the * Deletes all the session information being maintained for the specified <code>sessionId</code>. If the

View File

@ -13,7 +13,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.security.web.concurrent; package org.springframework.security.authentication.concurrent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -24,15 +24,10 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import javax.servlet.http.HttpSession;
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.context.ApplicationEvent;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.springframework.security.authentication.concurrent.SessionInformation; import org.springframework.security.core.session.SessionDestroyedEvent;
import org.springframework.security.authentication.concurrent.SessionRegistry;
import org.springframework.security.web.session.HttpSessionDestroyedEvent;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
@ -48,7 +43,7 @@ import org.springframework.util.Assert;
* @author Ben Alex * @author Ben Alex
* @version $Id$ * @version $Id$
*/ */
public class SessionRegistryImpl implements SessionRegistry, ApplicationListener { public class SessionRegistryImpl implements SessionRegistry, ApplicationListener<SessionDestroyedEvent> {
//~ Static fields/initializers ===================================================================================== //~ Static fields/initializers =====================================================================================
protected static final Log logger = LogFactory.getLog(SessionRegistryImpl.class); protected static final Log logger = LogFactory.getLog(SessionRegistryImpl.class);
@ -98,11 +93,9 @@ public class SessionRegistryImpl implements SessionRegistry, ApplicationListener
return (SessionInformation) sessionIds.get(sessionId); return (SessionInformation) sessionIds.get(sessionId);
} }
public void onApplicationEvent(ApplicationEvent event) { public void onApplicationEvent(SessionDestroyedEvent event) {
if (event instanceof HttpSessionDestroyedEvent) { String sessionId = event.getId();
String sessionId = ((HttpSession) event.getSource()).getId(); removeSessionInformation(sessionId);
removeSessionInformation(sessionId);
}
} }
public void refreshLastRequest(String sessionId) { public void refreshLastRequest(String sessionId) {

View File

@ -23,4 +23,10 @@ public abstract class SessionDestroyedEvent extends ApplicationEvent {
* @return the <tt>SecurityContext</tt> associated with the session, or null if there is no context. * @return the <tt>SecurityContext</tt> associated with the session, or null if there is no context.
*/ */
public abstract SecurityContext getSecurityContext(); public abstract SecurityContext getSecurityContext();
/**
* The identifier associated with the destroyed session.
* @return
*/
public abstract String getId();
} }

View File

@ -13,23 +13,19 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.security.web.concurrent; package org.springframework.security.authentication.concurrent;
import junit.framework.TestCase;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.concurrent.ConcurrentLoginException; import org.springframework.security.authentication.concurrent.ConcurrentLoginException;
import org.springframework.security.authentication.concurrent.ConcurrentSessionControllerImpl; import org.springframework.security.authentication.concurrent.ConcurrentSessionControllerImpl;
import org.springframework.security.authentication.concurrent.SessionIdentifierAware;
import org.springframework.security.authentication.concurrent.SessionRegistry; import org.springframework.security.authentication.concurrent.SessionRegistry;
import org.springframework.security.authentication.concurrent.SessionRegistryImpl;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.security.web.concurrent.SessionRegistryImpl;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpSession;
/** /**
* Tests {@link ConcurrentSessionControllerImpl}. * Tests {@link ConcurrentSessionControllerImpl}.
@ -37,25 +33,24 @@ import org.springframework.mock.web.MockHttpSession;
* @author Ben Alex * @author Ben Alex
* @version $Id$ * @version $Id$
*/ */
public class ConcurrentSessionControllerImplTests extends TestCase { public class ConcurrentSessionControllerImplTests {
//~ Methods ======================================================================================================== //~ Methods ========================================================================================================
private static int nextSessionId = 1000;
private Authentication createAuthentication(String user, String password) { private Authentication createAuthentication(String user, String password) {
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(user, password); UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(user, password);
auth.setDetails(createWebDetails(auth)); auth.setDetails(new SessionIdentifierAware() {
private final String id = Integer.toString(nextSessionId++);
public String getSessionId() {
return id;
}
});
return auth; return auth;
} }
private WebAuthenticationDetails createWebDetails(Authentication auth) { @Test
MockHttpSession session = new MockHttpSession();
MockHttpServletRequest request = new MockHttpServletRequest();
request.setSession(session);
request.setUserPrincipal(auth);
return new WebAuthenticationDetails(request);
}
public void testLifecycle() throws Exception { public void testLifecycle() throws Exception {
// Build a test fixture // Build a test fixture
ConcurrentSessionControllerImpl sc = new ConcurrentSessionControllerImpl(); ConcurrentSessionControllerImpl sc = new ConcurrentSessionControllerImpl();
@ -67,7 +62,7 @@ public class ConcurrentSessionControllerImplTests extends TestCase {
sc.checkAuthenticationAllowed(auth); sc.checkAuthenticationAllowed(auth);
sc.registerSuccessfulAuthentication(auth); sc.registerSuccessfulAuthentication(auth);
String sessionId1 = ((WebAuthenticationDetails) auth.getDetails()).getSessionId(); String sessionId1 = ((SessionIdentifierAware) auth.getDetails()).getSessionId();
assertFalse(registry.getSessionInformation(sessionId1).isExpired()); assertFalse(registry.getSessionInformation(sessionId1).isExpired());
// Attempt to authenticate again - it should still be successful // Attempt to authenticate again - it should still be successful
@ -94,34 +89,24 @@ public class ConcurrentSessionControllerImplTests extends TestCase {
sc.checkAuthenticationAllowed(auth3); sc.checkAuthenticationAllowed(auth3);
sc.registerSuccessfulAuthentication(auth3); sc.registerSuccessfulAuthentication(auth3);
String sessionId3 = ((WebAuthenticationDetails) auth3.getDetails()).getSessionId(); String sessionId3 = ((SessionIdentifierAware) auth3.getDetails()).getSessionId();
assertTrue(registry.getSessionInformation(sessionId1).isExpired()); assertTrue(registry.getSessionInformation(sessionId1).isExpired());
assertFalse(registry.getSessionInformation(sessionId3).isExpired()); assertFalse(registry.getSessionInformation(sessionId3).isExpired());
} }
public void testStartupDetectsInvalidMaximumSessions() @Test(expected=IllegalArgumentException.class)
throws Exception { public void startupDetectsInvalidMaximumSessions() throws Exception {
ConcurrentSessionControllerImpl sc = new ConcurrentSessionControllerImpl(); ConcurrentSessionControllerImpl sc = new ConcurrentSessionControllerImpl();
sc.setMaximumSessions(0); sc.setMaximumSessions(0);
try { sc.afterPropertiesSet();
sc.afterPropertiesSet();
fail("Should have thrown IAE");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
} }
public void testStartupDetectsInvalidSessionRegistry() @Test(expected=IllegalArgumentException.class)
throws Exception { public void startupDetectsInvalidSessionRegistry() throws Exception {
ConcurrentSessionControllerImpl sc = new ConcurrentSessionControllerImpl(); ConcurrentSessionControllerImpl sc = new ConcurrentSessionControllerImpl();
sc.setSessionRegistry(null); sc.setSessionRegistry(null);
try { sc.afterPropertiesSet();
sc.afterPropertiesSet();
fail("Should have thrown IAE");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
} }
} }

View File

@ -13,7 +13,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.security.web.concurrent; package org.springframework.security.authentication.concurrent;
import junit.framework.TestCase; import junit.framework.TestCase;

View File

@ -13,49 +13,59 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.security.web.concurrent; package org.springframework.security.authentication.concurrent;
import junit.framework.TestCase; import static org.junit.Assert.*;
import org.springframework.security.authentication.concurrent.SessionInformation;
import org.springframework.security.web.concurrent.SessionRegistryImpl;
import org.springframework.security.web.session.HttpSessionDestroyedEvent;
import org.springframework.mock.web.MockHttpSession;
import java.util.Date; import java.util.Date;
import org.junit.Before;
import org.junit.Test;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.session.SessionDestroyedEvent;
/** /**
* Tests {@link SessionRegistryImpl}. * Tests {@link SessionRegistryImpl}.
* *
* @author Ben Alex * @author Ben Alex
* @version $Id$ * @version $Id$
*/ */
public class SessionRegistryImplTests extends TestCase { public class SessionRegistryImplTests {
private SessionRegistryImpl sessionRegistry; private SessionRegistryImpl sessionRegistry;
//~ Methods ======================================================================================================== //~ Methods ========================================================================================================
protected void setUp() throws Exception { @Before
public void setUp() throws Exception {
sessionRegistry = new SessionRegistryImpl(); sessionRegistry = new SessionRegistryImpl();
} }
public void testEventPublishing() { @Test
MockHttpSession httpSession = new MockHttpSession(); public void sessionDestroyedEventRemovesSessionFromRegistry() {
Object principal = "Some principal object"; Object principal = "Some principal object";
String sessionId = httpSession.getId(); final String sessionId = "zzzz";
assertNotNull(sessionId);
// Register new Session // Register new Session
sessionRegistry.registerNewSession(sessionId, principal); sessionRegistry.registerNewSession(sessionId, principal);
// Deregister session via an ApplicationEvent // De-register session via an ApplicationEvent
sessionRegistry.onApplicationEvent(new HttpSessionDestroyedEvent(httpSession)); sessionRegistry.onApplicationEvent(new SessionDestroyedEvent("") {
@Override
public String getId() {
return sessionId;
}
@Override
public SecurityContext getSecurityContext() {
return null;
}
});
// Check attempts to retrieve cleared session return null // Check attempts to retrieve cleared session return null
assertNull(sessionRegistry.getSessionInformation(sessionId)); assertNull(sessionRegistry.getSessionInformation(sessionId));
} }
@Test
public void testMultiplePrincipals() throws Exception { public void testMultiplePrincipals() throws Exception {
Object principal1 = "principal_1"; Object principal1 = "principal_1";
Object principal2 = "principal_2"; Object principal2 = "principal_2";
@ -71,6 +81,7 @@ public class SessionRegistryImplTests extends TestCase {
assertEquals(principal2, sessionRegistry.getAllPrincipals()[1]); assertEquals(principal2, sessionRegistry.getAllPrincipals()[1]);
} }
@Test
public void testSessionInformationLifecycle() throws Exception { public void testSessionInformationLifecycle() throws Exception {
Object principal = "Some principal object"; Object principal = "Some principal object";
String sessionId = "1234567890"; String sessionId = "1234567890";
@ -106,6 +117,7 @@ public class SessionRegistryImplTests extends TestCase {
assertNull(sessionRegistry.getAllSessions(principal, false)); assertNull(sessionRegistry.getAllSessions(principal, false));
} }
@Test
public void testTwoSessionsOnePrincipalExpiring() throws Exception { public void testTwoSessionsOnePrincipalExpiring() throws Exception {
Object principal = "Some principal object"; Object principal = "Some principal object";
String sessionId1 = "1234567890"; String sessionId1 = "1234567890";
@ -130,6 +142,7 @@ public class SessionRegistryImplTests extends TestCase {
assertFalse(sessionRegistry.getSessionInformation(sessionId1).isExpired()); assertFalse(sessionRegistry.getSessionInformation(sessionId1).isExpired());
} }
@Test
public void testTwoSessionsOnePrincipalHandling() throws Exception { public void testTwoSessionsOnePrincipalHandling() throws Exception {
Object principal = "Some principal object"; Object principal = "Some principal object";
String sessionId1 = "1234567890"; String sessionId1 = "1234567890";
@ -155,7 +168,7 @@ public class SessionRegistryImplTests extends TestCase {
assertNull(sessionRegistry.getAllSessions(principal, false)); assertNull(sessionRegistry.getAllSessions(principal, false));
} }
boolean contains(String sessionId, Object principal) { private boolean contains(String sessionId, Object principal) {
SessionInformation[] info = sessionRegistry.getAllSessions(principal, false); SessionInformation[] info = sessionRegistry.getAllSessions(principal, false);
for (int i = 0; i < info.length; i++) { for (int i = 0; i < info.length; i++) {

View File

@ -35,11 +35,17 @@ public class HttpSessionDestroyedEvent extends SessionDestroyedEvent {
super(session); super(session);
} }
public HttpSession getSession() {
return (HttpSession) getSource();
}
@Override
public SecurityContext getSecurityContext() { public SecurityContext getSecurityContext() {
return (SecurityContext) ((HttpSession)getSource()).getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY); return (SecurityContext) ((HttpSession)getSource()).getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
} }
public HttpSession getSession() { @Override
return (HttpSession) getSource(); public String getId() {
return getSession().getId();
} }
} }

View File

@ -21,8 +21,8 @@ import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockHttpSession; import org.springframework.mock.web.MockHttpSession;
import org.springframework.security.authentication.concurrent.SessionRegistry; import org.springframework.security.authentication.concurrent.SessionRegistry;
import org.springframework.security.authentication.concurrent.SessionRegistryImpl;
import org.springframework.security.web.concurrent.ConcurrentSessionFilter; import org.springframework.security.web.concurrent.ConcurrentSessionFilter;
import org.springframework.security.web.concurrent.SessionRegistryImpl;
import javax.servlet.Filter; import javax.servlet.Filter;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;