CASC-33
Add SingleSignOutValve, which required some refactoring in core org.jasig.cas.client.session package to facilitate code reuse.
This commit is contained in:
parent
5857589a44
commit
a947490c04
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue