|
@@ -15,11 +15,11 @@
|
|
|
|
|
|
package net.sf.acegisecurity.context;
|
|
|
|
|
|
-import net.sf.acegisecurity.Authentication;
|
|
|
-
|
|
|
import org.apache.commons.logging.Log;
|
|
|
import org.apache.commons.logging.LogFactory;
|
|
|
|
|
|
+import org.springframework.beans.factory.InitializingBean;
|
|
|
+
|
|
|
import java.io.IOException;
|
|
|
|
|
|
import javax.servlet.Filter;
|
|
@@ -34,27 +34,38 @@ import javax.servlet.http.HttpSession;
|
|
|
|
|
|
/**
|
|
|
* <p>
|
|
|
- * Populates the <code>SecurityContext</code> with information obtained from
|
|
|
- * the <code>HttpSession</code>.
|
|
|
+ * Populates the <code>SecurityContextHolder</code> with information obtained
|
|
|
+ * from the <code>HttpSession</code>.
|
|
|
* </p>
|
|
|
*
|
|
|
* <p>
|
|
|
* The <code>HttpSession</code> will be queried to retrieve the
|
|
|
- * <code>Authentication</code> that should be stored against the
|
|
|
- * <code>SecurityContext</code> for the duration of the web request. At the
|
|
|
- * end of the web request, any updates made to the
|
|
|
- * <code>SecurityContext</code> will be persisted back to the
|
|
|
+ * <code>SecurityContext</code> that should be stored against the
|
|
|
+ * <code>SecurityContextHolder</code> for the duration of the web request. At
|
|
|
+ * the end of the web request, any updates made to the
|
|
|
+ * <code>SecurityContextHolder</code> will be persisted back to the
|
|
|
* <code>HttpSession</code> by this filter.
|
|
|
* </p>
|
|
|
*
|
|
|
* <p>
|
|
|
+ * If a valid <code>SecurityContext</code> cannot be obtained from the
|
|
|
+ * <code>HttpSession</code> for whatever reason, a fresh
|
|
|
+ * <code>SecurityContext</code> will be created and used instead. The created
|
|
|
+ * object will be of the instance defined by the {@link #setContext(Class)}
|
|
|
+ * method (which defaults to {@link
|
|
|
+ * net.sf.acegisecurity.context.SecurityContextImpl}.
|
|
|
+ * </p>
|
|
|
+ *
|
|
|
+ * <p>
|
|
|
* No <code>HttpSession</code> will be created by this filter if one does not
|
|
|
* already exist. If at the end of the web request the
|
|
|
* <code>HttpSession</code> does not exist, a <code>HttpSession</code> will
|
|
|
* <b>only</b> be created if the current contents of
|
|
|
- * <code>SecurityContext</code> are not <code>null</code>. This avoids
|
|
|
- * needless <code>HttpSession</code> creation, but automates the storage of
|
|
|
- * changes made to the <code>SecurityContext</code>.
|
|
|
+ * <code>ContextHolder</code> are not {@link
|
|
|
+ * java.lang.Object#equals(java.lang.Object)} to a <code>new</code> instance
|
|
|
+ * of {@link #setContext(Class)}. This avoids needless
|
|
|
+ * <code>HttpSession</code> creation, but automates the storage of changes
|
|
|
+ * made to the <code>ContextHolder</code>.
|
|
|
* </p>
|
|
|
*
|
|
|
* <P>
|
|
@@ -68,30 +79,35 @@ import javax.servlet.http.HttpSession;
|
|
|
* similar clients that will never present the same <code>jsessionid</code>
|
|
|
* etc), the {@link #setAllowSessionCreation(boolean)} should be set to
|
|
|
* <code>false</code>. Only do this if you really need to conserve server
|
|
|
- * memory and ensure all classes using the <code>SecurityContext</code> are
|
|
|
- * designed to have no persistence of the <code>Authentication</code> between
|
|
|
- * web requests.
|
|
|
+ * memory and ensure all classes using the <code>ContextHolder</code> are
|
|
|
+ * designed to have no persistence of the <code>Context</code> between web
|
|
|
+ * requests.
|
|
|
* </p>
|
|
|
*
|
|
|
* <p>
|
|
|
- * This filter MUST appear BEFORE any other Acegi Security related filters,
|
|
|
- * because this filter WILL REMOVE any <code>Authentication</code> it finds in
|
|
|
- * the <code>SecurityContext</code>.
|
|
|
+ * This filter MUST be executed BEFORE any authentication procesing mechanisms.
|
|
|
+ * Authentication processing mechanisms (eg BASIC, CAS processing filters etc)
|
|
|
+ * expect the <code>ContextHolder</code> to contain a valid
|
|
|
+ * <code>SecureContext</code> by the time they execute.
|
|
|
* </p>
|
|
|
*
|
|
|
* @author Ben Alex
|
|
|
* @author Patrick Burleson
|
|
|
* @version $Id$
|
|
|
*/
|
|
|
-public class HttpSessionContextIntegrationFilter implements Filter {
|
|
|
+public class HttpSessionContextIntegrationFilter implements InitializingBean,
|
|
|
+ Filter {
|
|
|
//~ Static fields/initializers =============================================
|
|
|
|
|
|
protected static final Log logger = LogFactory.getLog(HttpSessionContextIntegrationFilter.class);
|
|
|
private static final String FILTER_APPLIED = "__acegi_session_integration_filter_applied";
|
|
|
- public static final String ACEGI_SECURITY_AUTHENTICATION_CONTEXT_KEY = "ACEGI_SECURITY_AUTHENTICATION_CONTEXT";
|
|
|
+ public static final String ACEGI_SECURITY_CONTEXT_KEY = "ACEGI_SECURITY_CONTEXT";
|
|
|
|
|
|
//~ Instance fields ========================================================
|
|
|
|
|
|
+ private Class context = SecurityContextImpl.class;
|
|
|
+ private Object contextObject;
|
|
|
+
|
|
|
/**
|
|
|
* Indicates if this filter can create a <code>HttpSession</code> if needed
|
|
|
* (sessions are always created sparingly, but setting this value to false
|
|
@@ -109,6 +125,24 @@ public class HttpSessionContextIntegrationFilter implements Filter {
|
|
|
return allowSessionCreation;
|
|
|
}
|
|
|
|
|
|
+ public void setContext(Class secureContext) {
|
|
|
+ this.context = secureContext;
|
|
|
+ }
|
|
|
+
|
|
|
+ public Class getContext() {
|
|
|
+ return context;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void afterPropertiesSet() throws Exception {
|
|
|
+ if ((this.context == null)
|
|
|
+ || (!SecurityContext.class.isAssignableFrom(this.context))) {
|
|
|
+ throw new IllegalArgumentException(
|
|
|
+ "context must be defined and implement SecurityContext (typically use net.sf.acegisecurity.context.SecurityContextImpl)");
|
|
|
+ }
|
|
|
+
|
|
|
+ this.contextObject = generateNewContext();
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Does nothing. We use IoC container lifecycle services instead.
|
|
|
*/
|
|
@@ -124,18 +158,6 @@ public class HttpSessionContextIntegrationFilter implements Filter {
|
|
|
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
|
|
|
}
|
|
|
|
|
|
- // Nullify the ThreadLocal if it currently contains data (it shouldn't)
|
|
|
- if (SecurityContext.getAuthentication() != null) {
|
|
|
- if (logger.isWarnEnabled()) {
|
|
|
- logger.warn(
|
|
|
- "SecurityContext should have been null but contained: '"
|
|
|
- + SecurityContext.getAuthentication()
|
|
|
- + "'; setting to null now");
|
|
|
- }
|
|
|
-
|
|
|
- SecurityContext.setAuthentication(null);
|
|
|
- }
|
|
|
-
|
|
|
HttpSession httpSession = null;
|
|
|
boolean httpSessionExistedAtStartOfRequest = false;
|
|
|
|
|
@@ -146,47 +168,53 @@ public class HttpSessionContextIntegrationFilter implements Filter {
|
|
|
if (httpSession != null) {
|
|
|
httpSessionExistedAtStartOfRequest = true;
|
|
|
|
|
|
- Object authenticationObject = httpSession.getAttribute(ACEGI_SECURITY_AUTHENTICATION_CONTEXT_KEY);
|
|
|
+ Object contextObject = httpSession.getAttribute(ACEGI_SECURITY_CONTEXT_KEY);
|
|
|
|
|
|
- if (authenticationObject != null) {
|
|
|
- // HttpSession provided an Authentication object
|
|
|
- if (authenticationObject instanceof Authentication) {
|
|
|
+ if (contextObject != null) {
|
|
|
+ if (contextObject instanceof SecurityContext) {
|
|
|
if (logger.isDebugEnabled()) {
|
|
|
logger.debug(
|
|
|
- "Obtained from ACEGI_SECURITY_AUTHENTICATION_CONTEXT a valid Authentication and set to SecurityContext: '"
|
|
|
- + authenticationObject + "'");
|
|
|
+ "Obtained from ACEGI_SECURITY_CONTEXT a valid SecurityContext and set to SecurityContextHolder: '"
|
|
|
+ + contextObject + "'");
|
|
|
}
|
|
|
|
|
|
- SecurityContext.setAuthentication((Authentication) authenticationObject);
|
|
|
+ SecurityContextHolder.setContext((SecurityContext) contextObject);
|
|
|
} else {
|
|
|
if (logger.isWarnEnabled()) {
|
|
|
logger.warn(
|
|
|
- "ACEGI_SECURITY_AUTHENTICATION_CONTEXT did not contain an Authentication but contained: '"
|
|
|
- + authenticationObject
|
|
|
- + "'; are you improperly modifying the HttpSession directly (you should always use SecurityContext) or using the HttpSession attribute reserved for this class?");
|
|
|
+ "ACEGI_SECURITY_CONTEXT did not contain a SecurityContext but contained: '"
|
|
|
+ + contextObject
|
|
|
+ + "'; are you improperly modifying the HttpSession directly (you should always use SecurityContextHolder) or using the HttpSession attribute reserved for this class? - new SecurityContext instance associated with SecurityContextHolder");
|
|
|
}
|
|
|
+
|
|
|
+ SecurityContextHolder.setContext(generateNewContext());
|
|
|
}
|
|
|
} else {
|
|
|
if (logger.isDebugEnabled()) {
|
|
|
logger.debug(
|
|
|
- "HttpSession returned null object for ACEGI_SECURITY_AUTHENTICATION_CONTEXT");
|
|
|
+ "HttpSession returned null object for ACEGI_SECURITY_CONTEXT - new SecurityContext instance associated with SecurityContextHolder");
|
|
|
}
|
|
|
+
|
|
|
+ SecurityContextHolder.setContext(generateNewContext());
|
|
|
}
|
|
|
} else {
|
|
|
if (logger.isDebugEnabled()) {
|
|
|
- logger.debug("No HttpSession currently exists");
|
|
|
+ logger.debug(
|
|
|
+ "No HttpSession currently exists - new SecurityContext instance associated with SecurityContextHolder");
|
|
|
}
|
|
|
+
|
|
|
+ SecurityContextHolder.setContext(generateNewContext());
|
|
|
}
|
|
|
|
|
|
- // Make the HttpSession null, as we want to ensure we don't keep any
|
|
|
- // reference to the HttpSession laying around in memory (in case the
|
|
|
- // chain.doFilter() we're about to invoke decides to invalidate it).
|
|
|
+ // Make the HttpSession null, as we want to ensure we don't keep
|
|
|
+ // a reference to the HttpSession laying around in case the
|
|
|
+ // chain.doFilter() invalidates it.
|
|
|
httpSession = null;
|
|
|
|
|
|
// Proceed with chain
|
|
|
chain.doFilter(request, response);
|
|
|
|
|
|
- // Store Authentication back to HttpSession
|
|
|
+ // Store context back to HttpSession
|
|
|
try {
|
|
|
httpSession = ((HttpServletRequest) request).getSession(false);
|
|
|
} catch (IllegalStateException ignored) {}
|
|
@@ -194,21 +222,22 @@ public class HttpSessionContextIntegrationFilter implements Filter {
|
|
|
if ((httpSession == null) && httpSessionExistedAtStartOfRequest) {
|
|
|
if (logger.isDebugEnabled()) {
|
|
|
logger.debug(
|
|
|
- "HttpSession is now null, but was not null at start of request; session was invalidated during filter chain, so we will NOT create a new session now");
|
|
|
+ "HttpSession is now null, but was not null at start of request; session was invalidated, so do not create a new session");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // Generate a HttpSession *only* if we have to
|
|
|
+ // Generate a HttpSession only if we need to
|
|
|
if ((httpSession == null) && !httpSessionExistedAtStartOfRequest) {
|
|
|
if (!allowSessionCreation) {
|
|
|
if (logger.isDebugEnabled()) {
|
|
|
logger.debug(
|
|
|
- "The HttpSessionContextIntegrationFilter is prohibited from creating a HttpSession by the allowSessionCreation property being false");
|
|
|
+ "The HttpSession is currently null, and the HttpSessionContextIntegrationFilter is prohibited from creating a HttpSession (because the allowSessionCreation property is false) - SecurityContext thus not stored for next request");
|
|
|
}
|
|
|
- } else if (SecurityContext.getAuthentication() != null) {
|
|
|
+ } else if (!contextObject.equals(
|
|
|
+ SecurityContextHolder.getContext())) {
|
|
|
if (logger.isDebugEnabled()) {
|
|
|
logger.debug(
|
|
|
- "HttpSession being created as SecurityContext contents are non-null");
|
|
|
+ "HttpSession being created as SecurityContextHolder contents are non-default");
|
|
|
}
|
|
|
|
|
|
try {
|
|
@@ -217,32 +246,44 @@ public class HttpSessionContextIntegrationFilter implements Filter {
|
|
|
} else {
|
|
|
if (logger.isDebugEnabled()) {
|
|
|
logger.debug(
|
|
|
- "SecurityContext contents and HttpSession are both null; not creating HttpSession");
|
|
|
+ "HttpSession is null, but SecurityContextHolder has not changed from default: ' "
|
|
|
+ + SecurityContextHolder.getContext()
|
|
|
+ + "'; not creating HttpSession or storing SecurityContextHolder contents");
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // If HttpSession exists or was just created, store current SecurityContext contents
|
|
|
+ // If HttpSession exists, store current SecurityContextHolder contents
|
|
|
if (httpSession != null) {
|
|
|
- httpSession.setAttribute(ACEGI_SECURITY_AUTHENTICATION_CONTEXT_KEY,
|
|
|
- SecurityContext.getAuthentication());
|
|
|
+ httpSession.setAttribute(ACEGI_SECURITY_CONTEXT_KEY,
|
|
|
+ SecurityContextHolder.getContext());
|
|
|
|
|
|
if (logger.isDebugEnabled()) {
|
|
|
logger.debug("SecurityContext stored to HttpSession: '"
|
|
|
- + SecurityContext.getAuthentication() + "'");
|
|
|
+ + SecurityContextHolder.getContext() + "'");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // Remove SecurityContext contents, ready for next request
|
|
|
- SecurityContext.setAuthentication(null);
|
|
|
+ // Remove SecurityContextHolder contents
|
|
|
+ SecurityContextHolder.setContext(generateNewContext());
|
|
|
|
|
|
if (logger.isDebugEnabled()) {
|
|
|
logger.debug(
|
|
|
- "SecurityContext set to null as request processing completed");
|
|
|
+ "SecurityContextHolder set to new context, as request processing completed");
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ public SecurityContext generateNewContext() throws ServletException {
|
|
|
+ try {
|
|
|
+ return (SecurityContext) this.context.newInstance();
|
|
|
+ } catch (InstantiationException ie) {
|
|
|
+ throw new ServletException(ie);
|
|
|
+ } catch (IllegalAccessException iae) {
|
|
|
+ throw new ServletException(iae);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Does nothing. We use IoC container lifecycle services instead.
|
|
|
*
|