Add SingleSignOutValve, which required some refactoring in core
org.jasig.cas.client.session package to facilitate code reuse.
This commit is contained in:
Marvin S. Addison 2010-09-23 15:03:50 +00:00
parent 5857589a44
commit a947490c04
5 changed files with 355 additions and 83 deletions

View File

@ -8,8 +8,6 @@ package org.jasig.cas.client.session;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasig.cas.client.util.AbstractConfigurationFilter;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.util.XmlUtils;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
@ -17,7 +15,6 @@ import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
@ -28,97 +25,51 @@ import java.io.IOException;
* @since 3.1
*/
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();
private static Log log = LogFactory.getLog(SingleSignOutFilter.class);
private static final SingleSignOutHandler handler = new SingleSignOutHandler();
public void init(final FilterConfig filterConfig) throws ServletException {
if (!isIgnoreInitConfiguration()) {
setArtifactParameterName(getPropertyFromInitParams(filterConfig, "artifactParameterName", "ticket"));
handler.setArtifactParameterName(getPropertyFromInitParams(filterConfig, "artifactParameterName", "ticket"));
handler.setLogoutParameterName(getPropertyFromInitParams(filterConfig, "logoutParameterName", "logoutRequest"));
}
init();
handler.init();
}
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 name) {
handler.setArtifactParameterName(name);
}
public void setLogoutParameterName(final String name) {
handler.setLogoutParameterName(name);
}
public void setArtifactParameterName(final String artifactParameterName) {
this.artifactParameterName = artifactParameterName;
public void setSessionMappingStorage(final SessionMappingStorage storage) {
handler.setSessionMappingStorage(storage);
}
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) servletRequest;
if ("POST".equals(request.getMethod())) {
final String logoutRequest = CommonUtils.safeGetParameter(request, "logoutRequest");
if (CommonUtils.isNotBlank(logoutRequest)) {
if (log.isTraceEnabled()) {
log.trace ("Logout request=[" + logoutRequest + "]");
}
final String sessionIdentifier = XmlUtils.getTextForElement(logoutRequest, "SessionIndex");
if (CommonUtils.isNotBlank(sessionIdentifier)) {
final HttpSession session = SESSION_MAPPING_STORAGE.removeSessionByMappingId(sessionIdentifier);
if (session != null) {
String sessionID = session.getId();
if (log.isDebugEnabled()) {
log.debug ("Invalidating session [" + sessionID + "] for ST [" + sessionIdentifier + "]");
}
try {
session.invalidate();
} catch (final IllegalStateException e) {
log.debug(e,e);
}
}
return;
}
}
if (handler.isTokenRequest(request)) {
handler.recordSession(request);
} else if (handler.isLogoutRequest(request)) {
handler.destroySession(request);
// Do not continue up filter chain
return;
} else {
final String artifact = CommonUtils.safeGetParameter(request, this.artifactParameterName);
if (CommonUtils.isNotBlank(artifact)) {
final HttpSession session = request.getSession(true);
if (log.isDebugEnabled()) {
log.debug("Storing session identifier for " + session.getId());
}
try {
SESSION_MAPPING_STORAGE.removeBySessionById(session.getId());
} catch (final Exception e) {
// ignore if the session is already marked as invalid. Nothing we can do!
}
SESSION_MAPPING_STORAGE.addSessionById(artifact, session);
} else {
log.debug("No Artifact Provided; no action taking place.");
}
log.trace("Ignoring URI " + request.getRequestURI());
}
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
}
protected static SingleSignOutHandler getSingleSignOutHandler() {
return handler;
}
}

View File

@ -0,0 +1,157 @@
/*
* Copyright 2010 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.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.util.XmlUtils;
/**
* Performs CAS single sign-out operations in an API-agnostic fashion.
*
* @author Marvin S. Addison
* @version $Revision$
*
*/
public class SingleSignOutHandler {
/** Logger instance */
private final Log log = LogFactory.getLog(getClass());
/** Mapping of token IDs and session IDs to HTTP sessions */
private SessionMappingStorage sessionMappingStorage = new HashMapBackedSessionMappingStorage();
/** The name of the artifact parameter. This is used to capture the session identifier. */
private String artifactParameterName = "ticket";
/** Parameter name that stores logout request */
private String logoutParameterName = "logoutRequest";
public void setSessionMappingStorage(final SessionMappingStorage storage) {
this.sessionMappingStorage = storage;
}
public SessionMappingStorage getSessionMappingStorage() {
return this.sessionMappingStorage;
}
/**
* @return Name of the parameter containing the authentication token.
*/
public String getArtifactParameterName() {
return artifactParameterName;
}
/**
* @param name Name of the authentication token parameter.
*/
public void setArtifactParameterName(final String name) {
this.artifactParameterName = name;
}
/**
* @return Name of parameter containing CAS logout request message.
*/
public String getLogoutParameterName() {
return logoutParameterName;
}
/**
* @param name Name of parameter containing CAS logout request message.
*/
public void setLogoutParameterName(final String name) {
this.logoutParameterName = name;
}
/**
* Initializes the component for use.
*/
public void init() {
CommonUtils.assertNotNull(this.artifactParameterName, "artifactParameterName cannot be null.");
CommonUtils.assertNotNull(this.logoutParameterName, "logoutParameterName cannot be null.");
CommonUtils.assertNotNull(this.sessionMappingStorage, "sessionMappingStorage cannote be null.");
}
/**
* Determines whether the given request contains an authentication token.
*
* @param request HTTP reqest.
*
* @return True if request contains authentication token, false otherwise.
*/
public boolean isTokenRequest(final HttpServletRequest request) {
return CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.artifactParameterName));
}
/**
* Determines whether the given request is a CAS logout request.
*
* @param request HTTP request.
*
* @return True if request is logout request, false otherwise.
*/
public boolean isLogoutRequest(final HttpServletRequest request) {
return "POST".equals(request.getMethod()) &&
CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.logoutParameterName));
}
/**
* Associates a token request with the current HTTP session by recording the mapping
* in the the configured {@link SessionMappingStorage} container.
*
* @param request HTTP request containing an authentication token.
*/
public void recordSession(final HttpServletRequest request) {
final HttpSession session = request.getSession(true);
final String token = CommonUtils.safeGetParameter(request, this.artifactParameterName);
if (log.isDebugEnabled()) {
log.debug("Recording session for token " + token);
}
try {
this.sessionMappingStorage.removeBySessionById(session.getId());
} catch (final Exception e) {
// ignore if the session is already marked as invalid. Nothing we can do!
}
sessionMappingStorage.addSessionById(
CommonUtils.safeGetParameter(request, this.artifactParameterName), session);
}
/**
* Destroys the current HTTP session for the given CAS logout request.
*
* @param request HTTP request containing a CAS logout message.
*/
public void destroySession(final HttpServletRequest request) {
final String logoutMessage = CommonUtils.safeGetParameter(request, this.logoutParameterName);
if (log.isTraceEnabled()) {
log.trace ("Logout request:\n" + logoutMessage);
}
final String token = XmlUtils.getTextForElement(logoutMessage, "SessionIndex");
if (CommonUtils.isNotBlank(token)) {
final HttpSession session = this.sessionMappingStorage.removeSessionByMappingId(token);
if (session != null) {
String sessionID = session.getId();
if (log.isDebugEnabled()) {
log.debug ("Invalidating session [" + sessionID + "] for token [" + token + "]");
}
try {
session.invalidate();
} catch (final IllegalStateException e) {
log.debug("Error invalidating session.", e);
}
}
}
}
}

View File

@ -9,9 +9,6 @@ import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* 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.
@ -24,19 +21,18 @@ import org.apache.commons.logging.LogFactory;
*/
public final class SingleSignOutHttpSessionListener implements HttpSessionListener {
private SessionMappingStorage SESSION_MAPPING_STORAGE;
private SessionMappingStorage sessionMappingStorage;
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();
if (sessionMappingStorage == null) {
sessionMappingStorage = getSessionMappingStorage();
}
final HttpSession session = event.getSession();
SESSION_MAPPING_STORAGE.removeBySessionById(session.getId());
sessionMappingStorage.removeBySessionById(session.getId());
}
/**
@ -46,6 +42,6 @@ public final class SingleSignOutHttpSessionListener implements HttpSessionListen
* @return the SessionMappingStorage
*/
protected static SessionMappingStorage getSessionMappingStorage() {
return SingleSignOutFilter.getSessionMappingStorage();
return SingleSignOutFilter.getSingleSignOutHandler().getSessionMappingStorage();
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright 2010 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.tomcat.v6;
import java.io.IOException;
import javax.servlet.ServletException;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.Session;
import org.apache.catalina.SessionEvent;
import org.apache.catalina.SessionListener;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.jasig.cas.client.session.SessionMappingStorage;
import org.jasig.cas.client.session.SingleSignOutHandler;
/**
* Handles logout request messages sent from the CAS server by ending the current
* HTTP session.
*
* @author Marvin S. Addison
* @version $Revision$
*
*/
public class SingleSignOutValve extends AbstractLifecycleValve implements SessionListener {
private static final String NAME = SingleSignOutValve.class.getName();
private final SingleSignOutHandler handler = new SingleSignOutHandler();
public void setArtifactParameterName(final String name) {
handler.setArtifactParameterName(name);
}
public void setLogoutParameterName(final String name) {
handler.setLogoutParameterName(name);
}
public void setSessionMappingStorage(final SessionMappingStorage storage) {
handler.setSessionMappingStorage(storage);
}
/** {@inheritDoc} */
public void start() throws LifecycleException {
super.start();
handler.init();
this.log.info("Startup completed.");
}
/** {@inheritDoc} */
public void invoke(final Request request, final Response response) throws IOException, ServletException {
if (this.handler.isTokenRequest(request)) {
this.handler.recordSession(request);
request.getSessionInternal(true).addSessionListener(this);
}
else if (this.handler.isLogoutRequest(request)) {
this.handler.destroySession(request);
// Do not proceed up valve chain
return;
} else {
this.log.debug("Ignoring URI " + request.getRequestURI());
}
getNext().invoke(request, response);
}
/** {@inheritDoc} */
public void sessionEvent(final SessionEvent event) {
if (Session.SESSION_DESTROYED_EVENT.equals(event.getType())) {
this.log.debug("Cleaning up SessionMappingStorage on destroySession event");
this.handler.getSessionMappingStorage().removeBySessionById(event.getSession().getId());
}
}
/** {@inheritDoc} */
protected String getName() {
return NAME;
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright 2010 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.tomcat.v7;
import java.io.IOException;
import javax.servlet.ServletException;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.Session;
import org.apache.catalina.SessionEvent;
import org.apache.catalina.SessionListener;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.ValveBase;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasig.cas.client.session.SessionMappingStorage;
import org.jasig.cas.client.session.SingleSignOutHandler;
/**
* Handles logout request messages sent from the CAS server by ending the current
* HTTP session.
*
* @author Marvin S. Addison
* @version $Revision$
*
*/
public class SingleSignOutValve extends ValveBase implements SessionListener {
/** Logger instance */
private final Log log = LogFactory.getLog(getClass());
private final SingleSignOutHandler handler = new SingleSignOutHandler();
public void setArtifactParameterName(final String name) {
handler.setArtifactParameterName(name);
}
public void setLogoutParameterName(final String name) {
handler.setLogoutParameterName(name);
}
public void setSessionMappingStorage(final SessionMappingStorage storage) {
handler.setSessionMappingStorage(storage);
}
/** {@inheritDoc} */
public void invoke(final Request request, final Response response) throws IOException, ServletException {
if (this.handler.isTokenRequest(request)) {
this.handler.recordSession(request);
request.getSessionInternal(true).addSessionListener(this);
}
else if (this.handler.isLogoutRequest(request)) {
this.handler.destroySession(request);
// Do not proceed up valve chain
return;
} else {
this.log.debug("Ignoring URI " + request.getRequestURI());
}
getNext().invoke(request, response);
}
/** {@inheritDoc} */
public void sessionEvent(final SessionEvent event) {
if (Session.SESSION_DESTROYED_EVENT.equals(event.getType())) {
this.log.debug("Cleaning up SessionMappingStorage on destroySession event");
this.handler.getSessionMappingStorage().removeBySessionById(event.getSession().getId());
}
}
/** {@inheritDoc} */
protected void startInternal() throws LifecycleException {
super.startInternal();
this.log.info("Starting...");
handler.init();
this.log.info("Startup completed.");
}
}