1
0
mirror of synced 2026-08-04 09:17:02 +00:00

SEC-1695: Allow customization of the session key under which the SecurityContext is stored.

This commit is contained in:
Luke Taylor
2011-05-25 19:51:47 +01:00
parent 42e0e158b4
commit 6d04670f87
8 changed files with 108 additions and 65 deletions
@@ -8,6 +8,7 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import javax.servlet.http.HttpServletRequest;
@@ -19,7 +20,7 @@ import javax.servlet.http.HttpSession;
* between requests.
* <p>
* The {@code HttpSession} will be queried to retrieve the {@code SecurityContext} in the <tt>loadContext</tt>
* method (using the key {@link #SPRING_SECURITY_CONTEXT_KEY}). If a valid {@code SecurityContext} cannot be
* method (using the key {@link #SPRING_SECURITY_CONTEXT_KEY} by default). If a valid {@code SecurityContext} cannot be
* obtained from the {@code HttpSession} for whatever reason, a fresh {@code SecurityContext} will be created
* by calling by {@link SecurityContextHolder#createEmptyContext()} and this instance will be returned instead.
* <p>
@@ -50,6 +51,9 @@ import javax.servlet.http.HttpSession;
* @since 3.0
*/
public class HttpSessionSecurityContextRepository implements SecurityContextRepository {
/**
* The default key under which the security context will be stored in the session.
*/
public static final String SPRING_SECURITY_CONTEXT_KEY = "SPRING_SECURITY_CONTEXT";
protected final Log logger = LogFactory.getLog(this.getClass());
@@ -59,6 +63,7 @@ public class HttpSessionSecurityContextRepository implements SecurityContextRepo
private final Object contextObject = SecurityContextHolder.createEmptyContext();
private boolean allowSessionCreation = true;
private boolean disableUrlRewriting = false;
private String springSecurityContextKey = SPRING_SECURITY_CONTEXT_KEY;
private final AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
@@ -108,7 +113,7 @@ public class HttpSessionSecurityContextRepository implements SecurityContextRepo
return false;
}
return session.getAttribute(SPRING_SECURITY_CONTEXT_KEY) != null;
return session.getAttribute(springSecurityContextKey) != null;
}
/**
@@ -128,7 +133,7 @@ public class HttpSessionSecurityContextRepository implements SecurityContextRepo
// Session exists, so try to obtain a context from it.
Object contextFromSession = httpSession.getAttribute(SPRING_SECURITY_CONTEXT_KEY);
Object contextFromSession = httpSession.getAttribute(springSecurityContextKey);
if (contextFromSession == null) {
if (debug) {
@@ -141,7 +146,7 @@ public class HttpSessionSecurityContextRepository implements SecurityContextRepo
// We now have the security context object from the session.
if (!(contextFromSession instanceof SecurityContext)) {
if (logger.isWarnEnabled()) {
logger.warn("SPRING_SECURITY_CONTEXT did not contain a SecurityContext but contained: '"
logger.warn(springSecurityContextKey + " did not contain a SecurityContext but contained: '"
+ contextFromSession + "'; are you improperly modifying the HttpSession directly "
+ "(you should always use SecurityContextHolder) or using the HttpSession attribute "
+ "reserved for this class?");
@@ -151,7 +156,7 @@ public class HttpSessionSecurityContextRepository implements SecurityContextRepo
}
if (debug) {
logger.debug("Obtained a valid SecurityContext from SPRING_SECURITY_CONTEXT: '" + contextFromSession + "'");
logger.debug("Obtained a valid SecurityContext from " + springSecurityContextKey + ": '" + contextFromSession + "'");
}
// Everything OK. The only non-null return from this method.
@@ -212,6 +217,17 @@ public class HttpSessionSecurityContextRepository implements SecurityContextRepo
this.disableUrlRewriting = disableUrlRewriting;
}
/**
* Allows the session attribute name to be customized for this repository instance.
*
* @param springSecurityContextKey the key under which the security context will be stored. Defaults to
* {@link #SPRING_SECURITY_CONTEXT_KEY}.
*/
public void setSpringSecurityContextKey(String springSecurityContextKey) {
Assert.hasText(springSecurityContextKey, "springSecurityContextKey cannot be empty");
this.springSecurityContextKey = springSecurityContextKey;
}
//~ Inner Classes ==================================================================================================
/**
@@ -273,7 +289,7 @@ public class HttpSessionSecurityContextRepository implements SecurityContextRepo
if (httpSession != null) {
// SEC-1587 A non-anonymous context may still be in the session
httpSession.removeAttribute(SPRING_SECURITY_CONTEXT_KEY);
httpSession.removeAttribute(springSecurityContextKey);
}
return;
}
@@ -286,8 +302,8 @@ public class HttpSessionSecurityContextRepository implements SecurityContextRepo
// actually changed in this thread (see SEC-37, SEC-1307, SEC-1528)
if (httpSession != null) {
// We may have a new session, so check also whether the context attribute is set SEC-1561
if (contextChanged(context) || httpSession.getAttribute(SPRING_SECURITY_CONTEXT_KEY) == null) {
httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY, context);
if (contextChanged(context) || httpSession.getAttribute(springSecurityContextKey) == null) {
httpSession.setAttribute(springSecurityContextKey, context);
if (logger.isDebugEnabled()) {
logger.debug("SecurityContext stored to HttpSession: '" + context + "'");
@@ -17,10 +17,12 @@ package org.springframework.security.web.session;
import javax.servlet.http.HttpSession;
import com.sun.xml.internal.ws.encoding.ContentType;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.session.SessionDestroyedEvent;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import java.util.*;
/**
* Published by the {@link HttpSessionEventPublisher} when a HttpSession is created in the container
@@ -39,9 +41,23 @@ public class HttpSessionDestroyedEvent extends SessionDestroyedEvent {
return (HttpSession) getSource();
}
@SuppressWarnings("unchecked")
@Override
public SecurityContext getSecurityContext() {
return (SecurityContext) ((HttpSession)getSource()).getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
public List<SecurityContext> getSecurityContexts() {
HttpSession session = (HttpSession)getSource();
Enumeration<String> attributes = session.getAttributeNames();
ArrayList<SecurityContext> contexts = new ArrayList<SecurityContext>();
while(attributes.hasMoreElements()) {
Object attribute = attributes.nextElement();
if (attribute instanceof SecurityContext) {
contexts.add((SecurityContext) attribute);
}
}
return contexts;
}
@Override
@@ -47,9 +47,10 @@ public class HttpSessionSecurityContextRepositoryTests {
@Test
public void existingContextIsSuccessFullyLoadedFromSessionAndSavedBack() throws Exception {
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();
repo.setSpringSecurityContextKey("imTheContext");
MockHttpServletRequest request = new MockHttpServletRequest();
SecurityContextHolder.getContext().setAuthentication(testToken);
request.getSession().setAttribute(SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext());
request.getSession().setAttribute("imTheContext", SecurityContextHolder.getContext());
MockHttpServletResponse response = new MockHttpServletResponse();
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
SecurityContext context = repo.loadContext(holder);
@@ -57,7 +58,7 @@ public class HttpSessionSecurityContextRepositoryTests {
assertEquals(testToken, context.getAuthentication());
// Won't actually be saved as it hasn't changed, but go through the use case anyway
repo.saveContext(context, holder.getRequest(), holder.getResponse());
assertEquals(context, request.getSession().getAttribute(SPRING_SECURITY_CONTEXT_KEY));
assertEquals(context, request.getSession().getAttribute("imTheContext"));
}
// SEC-1528
@@ -113,33 +114,35 @@ public class HttpSessionSecurityContextRepositoryTests {
@Test
public void redirectCausesEarlySaveOfContext() throws Exception {
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();
repo.setSpringSecurityContextKey("imTheContext");
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
SecurityContextHolder.setContext(repo.loadContext(holder));
SecurityContextHolder.getContext().setAuthentication(testToken);
holder.getResponse().sendRedirect("/doesntmatter");
assertEquals(SecurityContextHolder.getContext(), request.getSession().getAttribute(SPRING_SECURITY_CONTEXT_KEY));
assertEquals(SecurityContextHolder.getContext(), request.getSession().getAttribute("imTheContext"));
assertTrue(((SaveContextOnUpdateOrErrorResponseWrapper)holder.getResponse()).isContextSaved());
repo.saveContext(SecurityContextHolder.getContext(), holder.getRequest(), holder.getResponse());
// Check it's still the same
assertEquals(SecurityContextHolder.getContext(), request.getSession().getAttribute(SPRING_SECURITY_CONTEXT_KEY));
assertEquals(SecurityContextHolder.getContext(), request.getSession().getAttribute("imTheContext"));
}
@Test
public void sendErrorCausesEarlySaveOfContext() throws Exception {
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();
repo.setSpringSecurityContextKey("imTheContext");
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
SecurityContextHolder.setContext(repo.loadContext(holder));
SecurityContextHolder.getContext().setAuthentication(testToken);
holder.getResponse().sendError(404);
assertEquals(SecurityContextHolder.getContext(), request.getSession().getAttribute(SPRING_SECURITY_CONTEXT_KEY));
assertEquals(SecurityContextHolder.getContext(), request.getSession().getAttribute("imTheContext"));
assertTrue(((SaveContextOnUpdateOrErrorResponseWrapper)holder.getResponse()).isContextSaved());
repo.saveContext(SecurityContextHolder.getContext(), holder.getRequest(), holder.getResponse());
// Check it's still the same
assertEquals(SecurityContextHolder.getContext(), request.getSession().getAttribute(SPRING_SECURITY_CONTEXT_KEY));
assertEquals(SecurityContextHolder.getContext(), request.getSession().getAttribute("imTheContext"));
}
@Test
@@ -188,15 +191,16 @@ public class HttpSessionSecurityContextRepositoryTests {
@Test
public void contextIsRemovedFromSessionIfCurrentContextIsEmpty() throws Exception {
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();
repo.setSpringSecurityContextKey("imTheContext");
MockHttpServletRequest request = new MockHttpServletRequest();
SecurityContext ctxInSession = SecurityContextHolder.createEmptyContext();
ctxInSession.setAuthentication(testToken);
request.getSession().setAttribute(SPRING_SECURITY_CONTEXT_KEY, ctxInSession);
request.getSession().setAttribute("imTheContext", ctxInSession);
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, new MockHttpServletResponse());
repo.loadContext(holder);
// Save an empty context
repo.saveContext(SecurityContextHolder.getContext(), holder.getRequest(), holder.getResponse());
assertNull(request.getSession().getAttribute(SPRING_SECURITY_CONTEXT_KEY));
assertNull(request.getSession().getAttribute("imTheContext"));
}
@Test