SEC-243: SessionRegistry.getAllSessions() now accepts an "includeExpiredSessions" argument.

This commit is contained in:
Ben Alex 2006-04-27 23:25:00 +00:00
parent d8a56d4e60
commit 0648c65b0b
3 changed files with 59 additions and 48 deletions

View File

@ -1,4 +1,4 @@
/* Copyright 2004, 2005 Acegi Technology Pty Limited /* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,30 +18,35 @@ package org.acegisecurity.concurrent;
import org.acegisecurity.AcegiMessageSource; import org.acegisecurity.AcegiMessageSource;
import org.acegisecurity.Authentication; import org.acegisecurity.Authentication;
import org.acegisecurity.AuthenticationException; import org.acegisecurity.AuthenticationException;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware; import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor; import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
* Base implementation of {@link ConcurrentSessionControllerImpl} which * Base implementation of {@link ConcurrentSessionControllerImpl} which
* prohibits simultaneous logins. * prohibits simultaneous logins.
*
* <p> * <p>
* By default uses {@link SessionRegistryImpl}, * By default uses {@link SessionRegistryImpl}, although any
* although any <code>SessionRegistry</code> may be used. * <code>SessionRegistry</code> may be used.
* </p> * </p>
* *
* @author Ben Alex * @author Ben Alex
* @version $Id$ * @version $Id$
*/ */
public class ConcurrentSessionControllerImpl implements ConcurrentSessionController, public class ConcurrentSessionControllerImpl
InitializingBean, MessageSourceAware { implements ConcurrentSessionController, InitializingBean,
MessageSourceAware {
//~ Instance fields ======================================================== //~ Instance fields ========================================================
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor(); protected MessageSourceAccessor messages = AcegiMessageSource
.getAccessor();
private SessionRegistry sessionRegistry = new SessionRegistryImpl(); private SessionRegistry sessionRegistry = new SessionRegistryImpl();
private boolean exceptionIfMaximumExceeded = false; private boolean exceptionIfMaximumExceeded = false;
private int maximumSessions = 1; private int maximumSessions = 1;
@ -51,7 +56,7 @@ public class ConcurrentSessionControllerImpl implements ConcurrentSessionControl
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {
Assert.notNull(sessionRegistry, "SessionRegistry required"); Assert.notNull(sessionRegistry, "SessionRegistry required");
Assert.isTrue(maximumSessions != 0, Assert.isTrue(maximumSessions != 0,
"MaximumLogins must be either -1 to allow unlimited logins, or a positive integer to specify a maximum"); "MaximumLogins must be either -1 to allow unlimited logins, or a positive integer to specify a maximum");
Assert.notNull(this.messages, "A message source must be set"); Assert.notNull(this.messages, "A message source must be set");
} }
@ -59,21 +64,22 @@ public class ConcurrentSessionControllerImpl implements ConcurrentSessionControl
* Allows subclasses to customise behaviour when too many sessions are * Allows subclasses to customise behaviour when too many sessions are
* detected. * detected.
* *
* @param sessionId the session ID of the present request * @param sessionId the session ID of the present request
* @param sessions either <code>null</code> or all unexpired sessions * @param sessions either <code>null</code> or all unexpired sessions
* associated with the principal * associated with the principal
* @param allowableSessions DOCUMENT ME! * @param allowableSessions DOCUMENT ME!
* @param registry an instance of the <code>SessionRegistry</code> for * @param registry an instance of the <code>SessionRegistry</code> for
* subclass use * subclass use
*
* @throws ConcurrentLoginException DOCUMENT ME! * @throws ConcurrentLoginException DOCUMENT ME!
*/ */
protected void allowableSessionsExceeded(String sessionId, protected void allowableSessionsExceeded(String sessionId,
SessionInformation[] sessions, int allowableSessions, SessionInformation[] sessions, int allowableSessions,
SessionRegistry registry) { SessionRegistry registry) {
if (exceptionIfMaximumExceeded || (sessions == null)) { if (exceptionIfMaximumExceeded || (sessions == null)) {
throw new ConcurrentLoginException(messages.getMessage( throw new ConcurrentLoginException(messages.getMessage(
"ConcurrentSessionControllerImpl.exceededAllowed", "ConcurrentSessionControllerImpl.exceededAllowed",
new Object[]{new Integer(allowableSessions)}, new Object[] {new Integer(allowableSessions)},
"Maximum sessions of {0} for this principal exceeded")); "Maximum sessions of {0} for this principal exceeded"));
} }
@ -82,8 +88,8 @@ public class ConcurrentSessionControllerImpl implements ConcurrentSessionControl
for (int i = 0; i < sessions.length; i++) { for (int i = 0; i < sessions.length; i++) {
if ((leastRecentlyUsed == null) if ((leastRecentlyUsed == null)
|| sessions[i].getLastRequest() || sessions[i].getLastRequest()
.before(leastRecentlyUsed.getLastRequest())) { .before(leastRecentlyUsed.getLastRequest())) {
leastRecentlyUsed = sessions[i]; leastRecentlyUsed = sessions[i];
} }
} }
@ -92,16 +98,17 @@ public class ConcurrentSessionControllerImpl implements ConcurrentSessionControl
} }
public void checkAuthenticationAllowed(Authentication request) public void checkAuthenticationAllowed(Authentication request)
throws AuthenticationException { throws AuthenticationException {
Assert.notNull(request, Assert.notNull(request,
"Authentication request cannot be null (violation of interface contract)"); "Authentication request cannot be null (violation of interface contract)");
Object principal = SessionRegistryUtils Object principal = SessionRegistryUtils
.obtainPrincipalFromAuthentication(request); .obtainPrincipalFromAuthentication(request);
String sessionId = SessionRegistryUtils String sessionId = SessionRegistryUtils
.obtainSessionIdFromAuthentication(request); .obtainSessionIdFromAuthentication(request);
SessionInformation[] sessions = sessionRegistry.getAllSessions(principal); SessionInformation[] sessions = sessionRegistry.getAllSessions(principal,
false);
int sessionCount = 0; int sessionCount = 0;
@ -111,7 +118,7 @@ public class ConcurrentSessionControllerImpl implements ConcurrentSessionControl
int allowableSessions = getMaximumSessionsForThisUser(request); int allowableSessions = getMaximumSessionsForThisUser(request);
Assert.isTrue(allowableSessions != 0, Assert.isTrue(allowableSessions != 0,
"getMaximumSessionsForThisUser() must return either -1 to allow unlimited logins, or a positive integer to specify a maximum"); "getMaximumSessionsForThisUser() must return either -1 to allow unlimited logins, or a positive integer to specify a maximum");
if (sessionCount < allowableSessions) { if (sessionCount < allowableSessions) {
return; return;
@ -124,41 +131,40 @@ public class ConcurrentSessionControllerImpl implements ConcurrentSessionControl
} }
} }
allowableSessionsExceeded(sessionId, sessions, allowableSessionsExceeded(sessionId, sessions, allowableSessions,
allowableSessions, sessionRegistry); sessionRegistry);
} }
/** /**
* Method intended for use by subclasses to override the maximum * Method intended for use by subclasses to override the maximum number of
* number of sessions that are permitted for a particular * sessions that are permitted for a particular authentication. The
* authentication. The default implementation simply returns the * default implementation simply returns the <code>maximumSessions</code>
* <code>maximumSessions</code> value for the bean. * value for the bean.
* *
* @param authentication to determine the maximum sessions for * @param authentication to determine the maximum sessions for
* @return either -1 meaning unlimited, or a positive integer to *
* limit (never zero) * @return either -1 meaning unlimited, or a positive integer to limit
* (never zero)
*/ */
protected int getMaximumSessionsForThisUser( protected int getMaximumSessionsForThisUser(Authentication authentication) {
Authentication authentication) {
return maximumSessions; return maximumSessions;
} }
public void registerSuccessfulAuthentication( public void registerSuccessfulAuthentication(Authentication authentication) {
Authentication authentication) {
Assert.notNull(authentication, Assert.notNull(authentication,
"Authentication cannot be null (violation of interface contract)"); "Authentication cannot be null (violation of interface contract)");
Object principal = SessionRegistryUtils Object principal = SessionRegistryUtils
.obtainPrincipalFromAuthentication(authentication); .obtainPrincipalFromAuthentication(authentication);
String sessionId = SessionRegistryUtils String sessionId = SessionRegistryUtils
.obtainSessionIdFromAuthentication(authentication); .obtainSessionIdFromAuthentication(authentication);
sessionRegistry.removeSessionInformation(sessionId); sessionRegistry.removeSessionInformation(sessionId);
sessionRegistry.registerNewSession(sessionId, principal); sessionRegistry.registerNewSession(sessionId, principal);
} }
public void setExceptionIfMaximumExceeded( public void setExceptionIfMaximumExceeded(
boolean exceptionIfMaximumExceeded) { boolean exceptionIfMaximumExceeded) {
this.exceptionIfMaximumExceeded = exceptionIfMaximumExceeded; this.exceptionIfMaximumExceeded = exceptionIfMaximumExceeded;
} }
@ -170,8 +176,7 @@ public class ConcurrentSessionControllerImpl implements ConcurrentSessionControl
this.messages = new MessageSourceAccessor(messageSource); this.messages = new MessageSourceAccessor(messageSource);
} }
public void setSessionRegistry( public void setSessionRegistry(SessionRegistry sessionRegistry) {
SessionRegistry sessionRegistry) {
this.sessionRegistry = sessionRegistry; this.sessionRegistry = sessionRegistry;
} }
} }

View File

@ -34,15 +34,20 @@ public interface SessionRegistry {
/** /**
* Obtains all the known sessions for the specified principal. Sessions * Obtains all the known sessions for the specified principal. Sessions
* that have expired or destroyed are not returned. * that have been destroyed are not returned. Sessions that have expired
* may be returned, depending on the passed argument.
* *
* @param principal to locate sessions for (should never be * @param principal to locate sessions for (should never be
* <code>null</code>) * <code>null</code>)
* @param includeExpiredSessions if <code>true</code>, the returned
* sessions will also include those that have expired for the
* principal
* *
* @return the unexpired and undestroyed sessions for this principal, or * @return the matching sessions for this principal, or <code>null</code>
* <code>null</code> if none were found * if none were found
*/ */
public SessionInformation[] getAllSessions(Object principal); public SessionInformation[] getAllSessions(Object principal,
boolean includeExpiredSessions);
/** /**
* Obtains the session information for the specified * Obtains the session information for the specified

View File

@ -64,7 +64,8 @@ public class SessionRegistryImpl implements SessionRegistry,
return principals.keySet().toArray(); return principals.keySet().toArray();
} }
public SessionInformation[] getAllSessions(Object principal) { public SessionInformation[] getAllSessions(Object principal,
boolean includeExpiredSessions) {
Set sessionsUsedByPrincipal = (Set) principals.get(principal); Set sessionsUsedByPrincipal = (Set) principals.get(principal);
if (sessionsUsedByPrincipal == null) { if (sessionsUsedByPrincipal == null) {
@ -79,7 +80,7 @@ public class SessionRegistryImpl implements SessionRegistry,
String sessionId = (String) iter.next(); String sessionId = (String) iter.next();
SessionInformation sessionInformation = getSessionInformation(sessionId); SessionInformation sessionInformation = getSessionInformation(sessionId);
if (!sessionInformation.isExpired()) { if (includeExpiredSessions || !sessionInformation.isExpired()) {
list.add(sessionInformation); list.add(sessionInformation);
} }
} }