SEC-399: Add support for invalidating the existing session on successful authentication.

This commit is contained in:
Luke Taylor 2007-09-17 15:54:07 +00:00
parent 0efa5c3090
commit 96eb11aadc

View File

@ -44,6 +44,10 @@ import org.springframework.util.Assert;
import java.io.IOException;
import java.util.Properties;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
@ -53,6 +57,7 @@ import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* Abstract processor of browser-based HTTP-based authentication requests.
@ -121,6 +126,13 @@ import javax.servlet.http.HttpServletResponse;
* authentication was unsuccessful, because this would generally be recorded via
* an <code>AuthenticationManager</code>-specific application event.
* </p>
* <p>The filter has an optional attribute <tt>invalidateSessionOnSuccessfulAuthentication</tt> that will invalidate
* the current session on successful authentication. This is to protect against session fixation attacks (see
* <a href="http://en.wikipedia.org/wiki/Session_fixation">this Wikipedia article</a> for more information).
* The behaviour is turned off by default. Additionally there is a property <tt>migrateInvalidatedSessionAttributes</tt>
* which tells if on session invalidation we are to migrate all session attributes from the old session to a newly
* created one. This is turned on by default, but not used unless <tt>invalidateSessionOnSuccessfulAuthentication</tt>
* is true.</p>
*
* @author Ben Alex
* @version $Id: AbstractProcessingFilter.java 1909 2007-06-19 04:08:19Z
@ -128,15 +140,13 @@ import javax.servlet.http.HttpServletResponse;
*/
public abstract class AbstractProcessingFilter implements Filter, InitializingBean, ApplicationEventPublisherAware,
MessageSourceAware {
// ~ Static fields/initializers
// =====================================================================================
//~ Static fields/initializers =====================================================================================
public static final String ACEGI_SAVED_REQUEST_KEY = "ACEGI_SAVED_REQUEST_KEY";
public static final String ACEGI_SECURITY_LAST_EXCEPTION_KEY = "ACEGI_SECURITY_LAST_EXCEPTION";
// ~ Instance fields
// ================================================================================================
//~ Instance fields ================================================================================================
protected ApplicationEventPublisher eventPublisher;
@ -198,8 +208,24 @@ public abstract class AbstractProcessingFilter implements Filter, InitializingBe
*/
private boolean useRelativeContext = false;
// ~ Methods
// ========================================================================================================
/**
* Tells if we on successful authentication should invalidate the
* current session. This is a common guard against session fixation attacks.
* Defaults to <code>false</code>.
*/
private boolean invalidateSessionOnSuccessfulAuthentication = false;
/**
* If {@link #invalidateSessionOnSuccessfulAuthentication} is true, this
* flag indicates that the session attributes of the session to be invalidated
* are to be migrated to the new session. Defaults to <code>true</code> since
* nothing will happpen unless {@link #invalidateSessionOnSuccessfulAuthentication}
* is true.
*/
private boolean migrateInvalidatedSessionAttributes = true;
//~ Methods ========================================================================================================
public void afterPropertiesSet() throws Exception {
Assert.hasLength(filterProcessesUrl, "filterProcessesUrl must be specified");
@ -465,7 +491,17 @@ public abstract class AbstractProcessingFilter implements Filter, InitializingBe
this.rememberMeServices = rememberMeServices;
}
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
public void setInvalidateSessionOnSuccessfulAuthentication(boolean invalidateSessionOnSuccessfulAuthentication) {
this.invalidateSessionOnSuccessfulAuthentication = invalidateSessionOnSuccessfulAuthentication;
}
public void setMigrateInvalidatedSessionAttributes(boolean migrateInvalidatedSessionAttributes) {
this.migrateInvalidatedSessionAttributes = migrateInvalidatedSessionAttributes;
}
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
Authentication authResult) throws IOException {
if (logger.isDebugEnabled()) {
logger.debug("Authentication success: " + authResult.toString());
@ -477,7 +513,12 @@ public abstract class AbstractProcessingFilter implements Filter, InitializingBe
logger.debug("Updated SecurityContextHolder to contain the following Authentication: '" + authResult + "'");
}
String targetUrl = determineTargetUrl(request);
if (invalidateSessionOnSuccessfulAuthentication) {
startNewSessionIfRequired(request);
}
String targetUrl = determineTargetUrl(request);
if (logger.isDebugEnabled()) {
logger.debug("Redirecting to target URL from HTTP Session (or default): " + targetUrl);
@ -495,7 +536,53 @@ public abstract class AbstractProcessingFilter implements Filter, InitializingBe
sendRedirect(request, response, targetUrl);
}
protected String determineTargetUrl(HttpServletRequest request) {
private void startNewSessionIfRequired(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session != null) {
if (!migrateInvalidatedSessionAttributes) {
if (logger.isDebugEnabled()) {
logger.debug("Invalidating session without migrating attributes.");
}
session.invalidate();
session = null;
// this is probably not necessary, but seems cleaner since
// there already was a session going.
request.getSession(true);
} else {
if (logger.isDebugEnabled()) {
logger.debug("Invalidating session and migrating attributes.");
}
HashMap migratedAttributes = new HashMap();
Enumeration enumer = session.getAttributeNames();
while (enumer.hasMoreElements()) {
String key = (String) enumer.nextElement();
migratedAttributes.put(key, session.getAttribute(key));
}
session.invalidate();
session = request.getSession(true); // we now have a new session
Iterator iter = migratedAttributes.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
session.setAttribute((String) entry.getKey(), entry.getValue());
}
}
}
}
protected String determineTargetUrl(HttpServletRequest request) {
// Don't attempt to obtain the url from the saved request if
// alwaysUsedefaultTargetUrl is set
String targetUrl = alwaysUseDefaultTargetUrl ? null : obtainFullRequestUrl(request);