diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/session/HashMapBackedSessionMappingStorage.java b/cas-client-core/src/main/java/org/jasig/cas/client/session/HashMapBackedSessionMappingStorage.java new file mode 100644 index 0000000..94ae42a --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/session/HashMapBackedSessionMappingStorage.java @@ -0,0 +1,55 @@ +/* + * Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.ja-sig.org/products/cas/overview/license/index.html + */ +package org.jasig.cas.client.session; + +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpSession; + +/** + * HashMap backed implementation of SessionMappingStorage. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.1 + * + */ +public final class HashMapBackedSessionMappingStorage implements + SessionMappingStorage { + + /** + * Maps the ID from the CAS server to the Session. + */ + private final Map MANAGED_SESSIONS = new HashMap(); + + /** + * Maps the Session ID to the key from the CAS Server. + */ + private final Map ID_TO_SESSION_KEY_MAPPING = new HashMap(); + + public void addSessionById(String mappingId, HttpSession session) { + ID_TO_SESSION_KEY_MAPPING.put(session.getId(), mappingId); + MANAGED_SESSIONS.put(mappingId, session); + + } + + public void removeBySessionById(String sessionId) { + final String key = (String) ID_TO_SESSION_KEY_MAPPING.get(sessionId); + MANAGED_SESSIONS.remove(key); + ID_TO_SESSION_KEY_MAPPING.remove(sessionId); + } + + public HttpSession removeSessionByMappingId(String mappingId) { + final HttpSession session = (HttpSession) MANAGED_SESSIONS.get(mappingId); + + if (session != null) { + removeBySessionById(session.getId()); + } + + return session; + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/session/SessionMappingStorage.java b/cas-client-core/src/main/java/org/jasig/cas/client/session/SessionMappingStorage.java new file mode 100644 index 0000000..d1ddafb --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/session/SessionMappingStorage.java @@ -0,0 +1,41 @@ +/* + * Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license + * distributed with this file and available online at + * http://www.ja-sig.org/products/cas/overview/license/index.html + */ +package org.jasig.cas.client.session; + +import javax.servlet.http.HttpSession; + +/** + * Stores the mapping between sessions and keys to be retrieved later. + * + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.1 + * + */ +public interface SessionMappingStorage { + + /** + * Remove the HttpSession based on the mappingId. + * + * @param mappingId the id the session is keyed under. + * @return the HttpSession if it exists. + */ + HttpSession removeSessionByMappingId(String mappingId); + + /** + * Remove a session by its Id. + * @param sessionId the id of the session. + */ + void removeBySessionById(String sessionId); + + /** + * Add a session by its mapping Id. + * @param mappingId the id to map the session to. + * @param session the HttpSession. + */ + void addSessionById(String mappingId, HttpSession session); + +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/session/SingleSignOutFilter.java b/cas-client-core/src/main/java/org/jasig/cas/client/session/SingleSignOutFilter.java index bc21579..5ea00a0 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/session/SingleSignOutFilter.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/session/SingleSignOutFilter.java @@ -31,6 +31,8 @@ public final class SingleSignOutFilter extends AbstractConfigurationFilter { * The name of the artifact parameter. This is used to capture the session identifier. */ private String artifactParameterName = "ticket"; + + private static SessionMappingStorage SESSION_MAPPING_STORAGE = new HashMapBackedSessionMappingStorage(); public void init(final FilterConfig filterConfig) throws ServletException { setArtifactParameterName(getPropertyFromInitParams(filterConfig, "artifactParameterName", "ticket")); @@ -40,6 +42,7 @@ public final class SingleSignOutFilter extends AbstractConfigurationFilter { public void init() { CommonUtils.assertNotNull(this.artifactParameterName, "artifactParameterName cannot be null."); + CommonUtils.assertNotNull(SESSION_MAPPING_STORAGE, "sessionMappingStorage cannote be null."); } public void setArtifactParameterName(final String artifactParameterName) { @@ -56,7 +59,11 @@ public final class SingleSignOutFilter extends AbstractConfigurationFilter { final String sessionIdentifier = XmlUtils.getTextForElement(logoutRequest, "SessionIndex"); if (CommonUtils.isNotBlank(sessionIdentifier)) { - SingleSignOutHttpSessionListener.removeSession(sessionIdentifier); + final HttpSession session = SESSION_MAPPING_STORAGE.removeSessionByMappingId(sessionIdentifier); + + if (session != null) { + session.invalidate(); + } return; } } @@ -64,12 +71,20 @@ public final class SingleSignOutFilter extends AbstractConfigurationFilter { final String artifact = request.getParameter(this.artifactParameterName); final HttpSession session = request.getSession(); if (CommonUtils.isNotBlank(artifact)) { - SingleSignOutHttpSessionListener.addSession(artifact, session); + SESSION_MAPPING_STORAGE.addSessionById(artifact, session); } } filterChain.doFilter(servletRequest, servletResponse); } + + public void setSessionMappingStorage(final SessionMappingStorage storage) { + SESSION_MAPPING_STORAGE = storage; + } + + public static SessionMappingStorage getSessionMappingStorage() { + return SESSION_MAPPING_STORAGE; + } public void destroy() { // nothing to do diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/session/SingleSignOutHttpSessionListener.java b/cas-client-core/src/main/java/org/jasig/cas/client/session/SingleSignOutHttpSessionListener.java index 78627c7..7661c21 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/session/SingleSignOutHttpSessionListener.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/session/SingleSignOutHttpSessionListener.java @@ -8,16 +8,12 @@ package org.jasig.cas.client.session; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; -import java.util.HashMap; -import java.util.Map; /** * Listener to detect when an HTTP session is destroyed and remove it from the map of * managed sessions. Also allows for the programmatic removal of sessions. *
* Enables the CAS Single Sign out feature. - *
- * Note that this class does not scale to multiple machines. * * Scott Battaglia * @version $Revision$ Date$ @@ -25,49 +21,27 @@ import java.util.Map; */ public final class SingleSignOutHttpSessionListener implements HttpSessionListener { - /** - * Maps the ID from the CAS server to the Session. - */ - private static final Map MANAGED_SESSIONS = new HashMap(); - - /** - * Maps the Session ID to the key from the CAS Server. - */ - private static final Map ID_TO_SESSION_KEY_MAPPING = new HashMap(); - + private SessionMappingStorage SESSION_MAPPING_STORAGE; + public void sessionCreated(final HttpSessionEvent event) { // nothing to do at the moment } public void sessionDestroyed(final HttpSessionEvent event) { + if (SESSION_MAPPING_STORAGE == null) { + SESSION_MAPPING_STORAGE = getSessionMappingStorage(); + } final HttpSession session = event.getSession(); - final String key = (String) ID_TO_SESSION_KEY_MAPPING.get(session.getId()); - MANAGED_SESSIONS.remove(key); - ID_TO_SESSION_KEY_MAPPING.remove(session.getId()); - } - - public static void addSession(final String key, final HttpSession value) { - ID_TO_SESSION_KEY_MAPPING.put(value.getId(), key); - MANAGED_SESSIONS.put(key, value); + SESSION_MAPPING_STORAGE.removeBySessionById(session.getId()); } /** - * Method to remove the session from the mapping based on the key returned from the - * CAS server. - * - * @param key the key to look up in the map of sessions. + * Obtains a {@link SessionMappingStorage} object. Assumes this method will always return the same + * instance of the object. It assumes this because it generally lazily calls the method. + * + * @return the SessionMappingStorage */ - public static void removeSession(final String key) { - final HttpSession session = (HttpSession) MANAGED_SESSIONS.get(key); - - if (session == null) { - return; - } - - final String id = session.getId(); - MANAGED_SESSIONS.remove(key); - ID_TO_SESSION_KEY_MAPPING.remove(id); - - session.invalidate(); + protected static SessionMappingStorage getSessionMappingStorage() { + return SingleSignOutFilter.getSessionMappingStorage(); } }