SEC-1039: Created new filter SecurityContextPersistenceFilter and SecurityContextRepository strategy to replace HttpSessionContextIntegrationFilter functionality.
This commit is contained in:
parent
789be71d8c
commit
4d81d750cd
|
@ -0,0 +1,39 @@
|
|||
package org.springframework.security.context;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Used to pass the incoming request to {@link SecurityContextRepository#loadContext(HttpRequestResponseHolder)},
|
||||
* allowing the method to swap the request for a wrapped version, as well as returning the <tt>SecurityContext</tt>
|
||||
* value.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.5
|
||||
*/
|
||||
public class HttpRequestResponseHolder {
|
||||
HttpServletRequest request;
|
||||
HttpServletResponse response;
|
||||
|
||||
public HttpRequestResponseHolder(HttpServletRequest request, HttpServletResponse response) {
|
||||
this.request = request;
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
HttpServletRequest getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
void setRequest(HttpServletRequest request) {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
HttpServletResponse getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
void setResponse(HttpServletResponse response) {
|
||||
this.response = response;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,329 @@
|
|||
package org.springframework.security.context;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.security.AuthenticationTrustResolver;
|
||||
import org.springframework.security.AuthenticationTrustResolverImpl;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* A <tt>SecurityContextRepository</tt> implementation which stores the security context in the HttpSession between
|
||||
* requests.
|
||||
* <p>
|
||||
* The <code>HttpSession</code> will be queried to retrieve the <code>SecurityContext</code> in the <tt>loadContext</tt>
|
||||
* method (using the key {@link #SPRING_SECURITY_CONTEXT_KEY}). 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 returned instead. The created object will be an instance of the class set using the
|
||||
* {@link #setContextClass(Class)} method. If this hasn't been set, a {@link SecurityContextImpl} will be returned.
|
||||
* <p>
|
||||
* When <tt>saveContext</tt> is called, the context will be stored under the same key, provided
|
||||
* <ol>
|
||||
* <li>The value has changed</li>
|
||||
* <li>The configured <tt>AuthenticationTrustResolver</tt> does not report that the contents represent an anonymous
|
||||
* user</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* With the standard configuration, no <code>HttpSession</code> will be created during <tt>loadContext</tt> if one does
|
||||
* not already exist. When <tt>saveContext</tt> is called at the end of the web request, and no session exists, a new
|
||||
* <code>HttpSession</code> will <b>only</b> be created if the supplied <tt>SecurityContext</tt> is not equal
|
||||
* to a <code>new</code> instance of the {@link #setContextClass(Class) contextClass} (or an empty
|
||||
* <tt>SecurityContextImpl</tt> if the class has not been set. This avoids needless <code>HttpSession</code> creation,
|
||||
* but automates the storage of changes made to the context during the request. Note that if
|
||||
* {@link SecurityContextPersistenceFilter} is configured to eagerly create sessions, then the session-minimisation
|
||||
* logic applied here will not make any difference. If you are using eager session creation, then you should
|
||||
* ensure that the <tt>allowSessionCreation</tt> property of this class is set to <tt>true</tt> (the default).
|
||||
* <p>
|
||||
* If for whatever reason no <code>HttpSession</code> should <b>ever</b> be created (e.g. Basic authentication is being
|
||||
* used or similar clients that will never present the same <code>jsessionid</code> etc), then
|
||||
* {@link #setAllowSessionCreation(boolean) allowSessionCreation} 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>SecurityContextHolder</code> are designed to have no persistence of the <code>SecurityContext</code>
|
||||
* between web requests.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.5
|
||||
*/
|
||||
public class HttpSessionSecurityContextRepository implements SecurityContextRepository {
|
||||
public static final String SPRING_SECURITY_CONTEXT_KEY = "SPRING_SECURITY_CONTEXT";
|
||||
|
||||
protected final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private Class<? extends SecurityContext> securityContextClass = null;
|
||||
/** SecurityContext instance used to check for equality with default (unauthenticated) content */
|
||||
private Object contextObject = new SecurityContextImpl();
|
||||
private boolean cloneFromHttpSession = false;
|
||||
private boolean allowSessionCreation = true;
|
||||
|
||||
private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
|
||||
|
||||
public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
|
||||
HttpServletRequest request = requestResponseHolder.getRequest();
|
||||
HttpServletResponse response = requestResponseHolder.getResponse();
|
||||
HttpSession httpSession = request.getSession(false);
|
||||
|
||||
SecurityContext context = readSecurityContextFromSession(httpSession);
|
||||
|
||||
if (context == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("No SecurityContext was available from the HttpSession: " + httpSession +". " +
|
||||
"A new one will be created.");
|
||||
}
|
||||
context = generateNewContext();
|
||||
|
||||
}
|
||||
|
||||
requestResponseHolder.setResponse(new SaveToSessionResponseWrapper(response, request,
|
||||
httpSession != null, context.hashCode()));
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {
|
||||
SaveToSessionResponseWrapper responseWrapper = (SaveToSessionResponseWrapper)response;
|
||||
// saveContext() might already be called by the response wrapper
|
||||
// if something in the chain called sendError() or sendRedirect(). This ensures we only call it
|
||||
// once per request.
|
||||
if (!responseWrapper.isContextSaved() ) {
|
||||
responseWrapper.saveContext(context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets the security context from the session (if available) and returns it.
|
||||
* <p>
|
||||
* If the session is null, the context object is null or the context object stored in the session
|
||||
* is not an instance of SecurityContext it will return null.
|
||||
* <p>
|
||||
* If <tt>cloneFromHttpSession</tt> is set to true, it will attempt to clone the context object
|
||||
* and return the cloned instance.
|
||||
*
|
||||
* @param httpSession the session obtained from the request.
|
||||
*/
|
||||
private SecurityContext readSecurityContextFromSession(HttpSession httpSession) {
|
||||
if (httpSession == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("No HttpSession currently exists");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Session exists, so try to obtain a context from it.
|
||||
|
||||
Object contextFromSession = httpSession.getAttribute(SPRING_SECURITY_CONTEXT_KEY);
|
||||
|
||||
if (contextFromSession == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("HttpSession returned null object for SPRING_SECURITY_CONTEXT");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// 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: '"
|
||||
+ contextFromSession + "'; are you improperly modifying the HttpSession directly "
|
||||
+ "(you should always use SecurityContextHolder) or using the HttpSession attribute "
|
||||
+ "reserved for this class?");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Clone if required (see SEC-356)
|
||||
if (cloneFromHttpSession) {
|
||||
contextFromSession = cloneContext(contextFromSession);
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Obtained a valid SecurityContext from SPRING_SECURITY_CONTEXT: '" + contextFromSession + "'");
|
||||
}
|
||||
|
||||
// Everything OK. The only non-null return from this method.
|
||||
|
||||
return (SecurityContext) contextFromSession;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param context the object which was stored under the security context key in the HttpSession.
|
||||
* @return the cloned SecurityContext object. Never null.
|
||||
*/
|
||||
private Object cloneContext(Object context) {
|
||||
Object clonedContext = null;
|
||||
Assert.isInstanceOf(Cloneable.class, context,
|
||||
"Context must implement Cloneable and provide a Object.clone() method");
|
||||
try {
|
||||
Method m = context.getClass().getMethod("clone", new Class[]{});
|
||||
if (!m.isAccessible()) {
|
||||
m.setAccessible(true);
|
||||
}
|
||||
clonedContext = m.invoke(context, new Object[]{});
|
||||
} catch (Exception ex) {
|
||||
ReflectionUtils.handleReflectionException(ex);
|
||||
}
|
||||
|
||||
return clonedContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* By default, returns an instance of {@link SecurityContextImpl}.
|
||||
* If a custom <tt>SecurityContext</tt> implementation is in use (i.e. the <tt>securityContextClass</tt> property
|
||||
* is set), it will attempt to invoke the no-args constructor on the supplied class instead and return the created
|
||||
* instance.
|
||||
*
|
||||
* @return a new SecurityContext instance. Never null.
|
||||
*/
|
||||
SecurityContext generateNewContext() {
|
||||
if (securityContextClass == null) {
|
||||
return new SecurityContextImpl();
|
||||
}
|
||||
|
||||
SecurityContext context = null;
|
||||
try {
|
||||
context = securityContextClass.newInstance();
|
||||
} catch (Exception e) {
|
||||
ReflectionUtils.handleReflectionException(e);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
void setSecurityContextClass(Class contextClass) {
|
||||
if (contextClass == null || (!SecurityContext.class.isAssignableFrom(contextClass))) {
|
||||
throw new IllegalArgumentException("securityContextClass must implement SecurityContext "
|
||||
+ "(typically use org.springframework.security.context.SecurityContextImpl; existing class is "
|
||||
+ contextClass + ")");
|
||||
}
|
||||
|
||||
this.securityContextClass = contextClass;
|
||||
contextObject = generateNewContext();
|
||||
}
|
||||
|
||||
void setCloneFromHttpSession(boolean cloneFromHttpSession) {
|
||||
this.cloneFromHttpSession = cloneFromHttpSession;
|
||||
}
|
||||
|
||||
void setAllowSessionCreation(boolean allowSessionCreation) {
|
||||
this.allowSessionCreation = allowSessionCreation;
|
||||
}
|
||||
|
||||
//~ Inner Classes ==================================================================================================
|
||||
|
||||
/**
|
||||
* Wrapper that is applied to every request/response to update the <code>HttpSession<code> with
|
||||
* the <code>SecurityContext</code> when a <code>sendError()</code> or <code>sendRedirect</code>
|
||||
* happens. See SEC-398.
|
||||
* <p>
|
||||
* Stores the necessary state from the start of the request in order to make a decision about whether
|
||||
* the security context has changed before saving it.
|
||||
*/
|
||||
class SaveToSessionResponseWrapper extends SaveContextOnUpdateOrErrorResponseWrapper {
|
||||
|
||||
private HttpServletRequest request;
|
||||
private boolean httpSessionExistedAtStartOfRequest;
|
||||
private int contextHashBeforeChainExecution;
|
||||
|
||||
/**
|
||||
* Takes the parameters required to call <code>saveContext()</code> successfully in
|
||||
* addition to the request and the response object we are wrapping.
|
||||
*
|
||||
* @param request the request object (used to obtain the session, if one exists).
|
||||
* @param httpSessionExistedAtStartOfRequest indicates whether there was a session in place before the
|
||||
* filter chain executed. If this is true, and the session is found to be null, this indicates that it was
|
||||
* invalidated during the request and a new session will now be created.
|
||||
* @param contextHashBeforeChainExecution the hashcode of the context before the filter chain executed.
|
||||
* The context will only be stored if it has a different hashcode, indicating that the context changed
|
||||
* during the request.
|
||||
*/
|
||||
SaveToSessionResponseWrapper(HttpServletResponse response, HttpServletRequest request,
|
||||
boolean httpSessionExistedAtStartOfRequest,
|
||||
int contextHashBeforeChainExecution) {
|
||||
super(response);
|
||||
this.request = request;
|
||||
this.httpSessionExistedAtStartOfRequest = httpSessionExistedAtStartOfRequest;
|
||||
this.contextHashBeforeChainExecution = contextHashBeforeChainExecution;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the supplied security context in the session (if available) and if it has changed since it was
|
||||
* set at the start of the request. If the AuthenticationTrustResolver identifies the current user as
|
||||
* anonymous, then the context will not be stored.
|
||||
*
|
||||
* @param context the context object obtained from the SecurityContextHolder after the request has
|
||||
* been processed by the filter chain. SecurityContextHolder.getContext() cannot be used to obtain
|
||||
* the context as it has already been cleared by the time this method is called.
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
void saveContext(SecurityContext context) {
|
||||
HttpSession httpSession = request.getSession(false);
|
||||
|
||||
if (httpSession == null) {
|
||||
if (httpSessionExistedAtStartOfRequest) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("HttpSession is now null, but was not null at start of request; "
|
||||
+ "session was invalidated, so do not create a new session");
|
||||
}
|
||||
} else {
|
||||
// Generate a HttpSession only if we need to
|
||||
|
||||
if (!allowSessionCreation) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("The HttpSession is currently null, and the "
|
||||
+ "HttpSessionContextIntegrationFilter is prohibited from creating an HttpSession "
|
||||
+ "(because the allowSessionCreation property is false) - SecurityContext thus not "
|
||||
+ "stored for next request");
|
||||
}
|
||||
} else if (!contextObject.equals(context)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("HttpSession being created as SecurityContextHolder contents are non-default");
|
||||
}
|
||||
|
||||
try {
|
||||
httpSession = request.getSession(true);
|
||||
} catch (IllegalStateException e) {
|
||||
// Response must already be committed, therefore can't create a new session
|
||||
}
|
||||
|
||||
} else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("HttpSession is null, but SecurityContextHolder has not changed from default: ' "
|
||||
+ context
|
||||
+ "'; not creating HttpSession or storing SecurityContextHolder contents");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If HttpSession exists, store current SecurityContextHolder contents but only if
|
||||
// the SecurityContext has actually changed (see JIRA SEC-37)
|
||||
if (httpSession != null && context.hashCode() != contextHashBeforeChainExecution) {
|
||||
// See SEC-766
|
||||
if (authenticationTrustResolver.isAnonymous(context.getAuthentication())) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("SecurityContext contents are anonymous - context will not be stored in HttpSession. ");
|
||||
}
|
||||
} else {
|
||||
httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY, context);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("SecurityContext stored to HttpSession: '" + context + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package org.springframework.security.context;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponseWrapper;
|
||||
|
||||
/**
|
||||
* Base class for response wrappers which encapsulate the logic for storing a security context and which
|
||||
* store the with the <code>SecurityContext</code> when a <code>sendError()</code> or <code>sendRedirect</code>
|
||||
* happens. See SEC-398.
|
||||
* <p>
|
||||
* Sub-classes should implement the {@link #saveContext(SecurityContext context)} method.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @author Marten Algesten
|
||||
* @version $Id$
|
||||
* @since 2.5
|
||||
*/
|
||||
abstract class SaveContextOnUpdateOrErrorResponseWrapper extends HttpServletResponseWrapper {
|
||||
|
||||
boolean contextSaved = false;
|
||||
|
||||
SaveContextOnUpdateOrErrorResponseWrapper(HttpServletResponse response) {
|
||||
super(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the logic for storing the security context.
|
||||
*
|
||||
* @param context the <tt>SecurityContext</tt> instance to store
|
||||
*/
|
||||
abstract void saveContext(SecurityContext context);
|
||||
|
||||
/**
|
||||
* Makes sure the session is updated before calling the
|
||||
* superclass <code>sendError()</code>
|
||||
*/
|
||||
public void sendError(int sc) throws IOException {
|
||||
doSaveContext();
|
||||
super.sendError(sc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure the session is updated before calling the
|
||||
* superclass <code>sendError()</code>
|
||||
*/
|
||||
public void sendError(int sc, String msg) throws IOException {
|
||||
doSaveContext();
|
||||
super.sendError(sc, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure the context is stored before calling the
|
||||
* superclass <code>sendRedirect()</code>
|
||||
*/
|
||||
public void sendRedirect(String location) throws IOException {
|
||||
doSaveContext();
|
||||
super.sendRedirect(location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls <code>saveContext()</code> with the current contents of the <tt>SecurityContextHolder</tt>.
|
||||
*/
|
||||
private void doSaveContext() {
|
||||
saveContext(SecurityContextHolder.getContext());
|
||||
contextSaved = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if the response wrapper has called <code>saveContext()</code> because of an error or redirect.
|
||||
*/
|
||||
public boolean isContextSaved() {
|
||||
return contextSaved;
|
||||
}
|
||||
|
||||
}
|
|
@ -21,20 +21,24 @@ import java.lang.reflect.Constructor;
|
|||
|
||||
|
||||
/**
|
||||
* Associates a given {@link SecurityContext} with the current execution thread.<p>This class provides a series of
|
||||
* static methods that delegate to an instance of {@link org.springframework.security.context.SecurityContextHolderStrategy}. The
|
||||
* purpose of the class is to provide a convenient way to specify the strategy that should be used for a given JVM.
|
||||
* Associates a given {@link SecurityContext} with the current execution thread.
|
||||
* <p>
|
||||
* This class provides a series of static methods that delegate to an instance of
|
||||
* {@link org.springframework.security.context.SecurityContextHolderStrategy}. The purpose of the class is to provide a
|
||||
* convenient way to specify the strategy that should be used for a given JVM.
|
||||
* This is a JVM-wide setting, since everything in this class is <code>static</code> to facilitate ease of use in
|
||||
* calling code.</p>
|
||||
* <p>To specify which strategy should be used, you must provide a mode setting. A mode setting is one of the
|
||||
* calling code.
|
||||
* <p>
|
||||
* To specify which strategy should be used, you must provide a mode setting. A mode setting is one of the
|
||||
* three valid <code>MODE_</code> settings defined as <code>static final</code> fields, or a fully qualified classname
|
||||
* to a concrete implementation of {@link org.springframework.security.context.SecurityContextHolderStrategy} that provides a
|
||||
* public no-argument constructor.</p>
|
||||
* <p>There are two ways to specify the desired strategy mode <code>String</code>. The first is to specify it via
|
||||
* to a concrete implementation of {@link org.springframework.security.context.SecurityContextHolderStrategy} that
|
||||
* provides a public no-argument constructor.
|
||||
* <p>
|
||||
* There are two ways to specify the desired strategy mode <code>String</code>. The first is to specify it via
|
||||
* the system property keyed on {@link #SYSTEM_PROPERTY}. The second is to call {@link #setStrategyName(String)}
|
||||
* before using the class. If neither approach is used, the class will default to using {@link #MODE_THREADLOCAL},
|
||||
* which is backwards compatible, has fewer JVM incompatibilities and is appropriate on servers (whereas {@link
|
||||
* #MODE_GLOBAL} is definitely inappropriate for server use).</p>
|
||||
* #MODE_GLOBAL} is definitely inappropriate for server use).
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
|
@ -75,7 +79,7 @@ public class SecurityContextHolder {
|
|||
}
|
||||
|
||||
/**
|
||||
* Primarily for troubleshooting purposes, this method shows how many times the class has reinitialized its
|
||||
* Primarily for troubleshooting purposes, this method shows how many times the class has re-initialized its
|
||||
* <code>SecurityContextHolderStrategy</code>.
|
||||
*
|
||||
* @return the count (should be one unless you've called {@link #setStrategyName(String)} to switch to an alternate
|
||||
|
@ -122,9 +126,9 @@ public class SecurityContextHolder {
|
|||
|
||||
/**
|
||||
* Changes the preferred strategy. Do <em>NOT</em> call this method more than once for a given JVM, as it
|
||||
* will reinitialize the strategy and adversely affect any existing threads using the old strategy.
|
||||
* will re-initialize the strategy and adversely affect any existing threads using the old strategy.
|
||||
*
|
||||
* @param strategyName the fully qualified classname of the strategy that should be used.
|
||||
* @param strategyName the fully qualified class name of the strategy that should be used.
|
||||
*/
|
||||
public static void setStrategyName(String strategyName) {
|
||||
SecurityContextHolder.strategyName = strategyName;
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
package org.springframework.security.context;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.springframework.security.ui.FilterChainOrder;
|
||||
import org.springframework.security.ui.SpringSecurityFilter;
|
||||
|
||||
/**
|
||||
* Populates the {@link SecurityContextHolder} with information obtained from
|
||||
* the configured {@link SecurityContextRepository} prior to the request and stores it back in the repository
|
||||
* once the request has completed. By default it uses an {@link HttpSessionSecurityContextRepository}. See this
|
||||
* class for information <tt>HttpSession</tt> related configuration options.
|
||||
* <p>
|
||||
* This filter will only execute once per request, to resolve servlet container (specifically Weblogic)
|
||||
* incompatibilities.
|
||||
* <p>
|
||||
* This filter MUST be executed BEFORE any authentication processing mechanisms. Authentication processing mechanisms
|
||||
* (e.g. BASIC, CAS processing filters etc) expect the <code>SecurityContextHolder</code> to contain a valid
|
||||
* <code>SecurityContext</code> by the time they execute.
|
||||
* <p>
|
||||
* This is essentially a refactoring of the old <tt>HttpSessionContextIntegrationFilter</tt> to delegate
|
||||
* the storage issues to a separate strategy, allowing for more customization in the way the security context is
|
||||
* maintained between requests.
|
||||
* <p>
|
||||
* The <tt>forceEagerSessionCreation</tt> property can be used to ensure that a session is always available before
|
||||
* the filter chain executes (the default is <code>false</code>, as this is resource intensive and not recommended).
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.5
|
||||
*/
|
||||
public class SecurityContextPersistenceFilter extends SpringSecurityFilter {
|
||||
|
||||
static final String FILTER_APPLIED = "__spring_security_scpf_applied";
|
||||
|
||||
private SecurityContextRepository repo = new HttpSessionSecurityContextRepository();
|
||||
|
||||
private boolean forceEagerSessionCreation = false;
|
||||
|
||||
@Override
|
||||
protected void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
|
||||
if (request.getAttribute(FILTER_APPLIED) != null) {
|
||||
// ensure that filter is only applied once per request
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
|
||||
|
||||
if (forceEagerSessionCreation) {
|
||||
HttpSession session = request.getSession();
|
||||
logger.debug("Eagerly created session: " + session.getId());
|
||||
}
|
||||
|
||||
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
|
||||
|
||||
try {
|
||||
SecurityContext contextBeforeChainExecution = repo.loadContext(holder);
|
||||
SecurityContextHolder.setContext(contextBeforeChainExecution);
|
||||
|
||||
chain.doFilter(holder.getRequest(), holder.getResponse());
|
||||
|
||||
} finally {
|
||||
SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
|
||||
// Crucial removal of SecurityContextHolder contents - do this before anything else.
|
||||
SecurityContextHolder.clearContext();
|
||||
repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("SecurityContextHolder now cleared, as request processing completed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setSecurityContextRepository(SecurityContextRepository repo) {
|
||||
this.repo = repo;
|
||||
}
|
||||
|
||||
void setForceEagerSessionCreation(boolean forceEagerSessionCreation) {
|
||||
this.forceEagerSessionCreation = forceEagerSessionCreation;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return FilterChainOrder.SECURITY_CONTEXT_FILTER;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package org.springframework.security.context;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Strategy used for persisting a {@link SecurityContext} between requests.
|
||||
* <p>
|
||||
* Used by {@link SecurityContextPersistenceFilter} to obtain the context which should be used for the current thread
|
||||
* of execution and to store the context once it has been removed from thread-local storage and the request has
|
||||
* completed.
|
||||
* <p>
|
||||
* The persistence mechanism used will depend on the implementation, but most commonly the <tt>HttpSession</tt> will
|
||||
* be used to store the context.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.5
|
||||
*
|
||||
* @see SecurityContextPersistenceFilter
|
||||
* @see HttpSessionSecurityContextRepository
|
||||
* @see SaveContextOnUpdateOrErrorResponseWrapper
|
||||
*/
|
||||
public interface SecurityContextRepository {
|
||||
|
||||
/**
|
||||
* Obtains the security context for the supplied request. For an unauthenticated user, an empty context
|
||||
* implementation should be returned. This method should not return null.
|
||||
* <p>
|
||||
* The use of the <tt>HttpRequestResponseHolder</tt> parameter allows implementations to return wrapped versions of
|
||||
* the request or response (or both), allowing them to access implementation-specific state for the request.
|
||||
* The values obtained from the holder will be passed on to the filter chain and also to the <tt>saveContext</tt>
|
||||
* method when it is finally called. Implementations may wish to return a subclass of
|
||||
* {@link SaveContextOnUpdateOrErrorResponseWrapper} as the response object, which guarantees that the context is
|
||||
* persisted when an error or redirect occurs.
|
||||
*
|
||||
* @param requestResponseHolder holder for the current request and response for which the context should be loaded.
|
||||
*
|
||||
* @return The security context which should be used for the current request, never null.
|
||||
*/
|
||||
SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder);
|
||||
|
||||
/**
|
||||
* Stores the security context on completion of a request.
|
||||
*
|
||||
* @param context the non-null context which was obtained f
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response);
|
||||
}
|
|
@ -22,7 +22,8 @@ public abstract class FilterChainOrder {
|
|||
|
||||
public static final int CHANNEL_FILTER = FILTER_CHAIN_FIRST;
|
||||
public static final int CONCURRENT_SESSION_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
|
||||
public static final int HTTP_SESSION_CONTEXT_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
|
||||
public static final int SECURITY_CONTEXT_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
|
||||
public static final int HTTP_SESSION_CONTEXT_FILTER = SECURITY_CONTEXT_FILTER;
|
||||
public static final int LOGOUT_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
|
||||
public static final int X509_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
|
||||
public static final int PRE_AUTH_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
package org.springframework.security.context;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.security.providers.TestingAuthenticationToken;
|
||||
|
||||
public class HttpSessionSecurityContextRepositoryTests {
|
||||
private final TestingAuthenticationToken testToken = new TestingAuthenticationToken("someone", "passwd", "ROLE_A");
|
||||
|
||||
@Test(expected=IllegalArgumentException.class)
|
||||
public void detectsInvalidContextClass() throws Exception {
|
||||
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();
|
||||
repo.setSecurityContextClass(String.class);
|
||||
}
|
||||
|
||||
@Test(expected=IllegalArgumentException.class)
|
||||
public void cannotSetNullContextClass() throws Exception {
|
||||
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();
|
||||
repo.setSecurityContextClass(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sessionIsntCreatedIfContextDoesntChange() throws Exception {
|
||||
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
|
||||
SecurityContext context = repo.loadContext(holder);
|
||||
assertNull(request.getSession(false));
|
||||
repo.saveContext(context, holder.getRequest(), holder.getResponse());
|
||||
assertNull(request.getSession(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sessionIsntCreatedIfAllowSessionCreationIsFalse() throws Exception {
|
||||
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();
|
||||
repo.setAllowSessionCreation(false);
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
|
||||
SecurityContext context = repo.loadContext(holder);
|
||||
// Change context
|
||||
context.setAuthentication(testToken);
|
||||
repo.saveContext(context, holder.getRequest(), holder.getResponse());
|
||||
assertNull(request.getSession(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void existingContextIsSuccessFullyLoadedFromSessionAndSavedBack() throws Exception {
|
||||
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
SecurityContextHolder.getContext().setAuthentication(testToken);
|
||||
request.getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext());
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
|
||||
SecurityContext context = repo.loadContext(holder);
|
||||
assertNotNull(context);
|
||||
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(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonSecurityContextInSessionIsIgnored() throws Exception {
|
||||
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
SecurityContextHolder.getContext().setAuthentication(testToken);
|
||||
request.getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, "NotASecurityContextInstance");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
|
||||
SecurityContext context = repo.loadContext(holder);
|
||||
assertNotNull(context);
|
||||
assertNull(context.getAuthentication());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sessionIsCreatedAndContextStoredWhenContextChanges() throws Exception {
|
||||
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
|
||||
SecurityContext context = repo.loadContext(holder);
|
||||
assertNull(request.getSession(false));
|
||||
// Simulate authentication during the request
|
||||
context.setAuthentication(testToken);
|
||||
repo.saveContext(context, holder.getRequest(), holder.getResponse());
|
||||
assertNotNull(request.getSession(false));
|
||||
assertEquals(context, request.getSession().getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void redirectCausesEarlySaveOfContext() throws Exception {
|
||||
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();
|
||||
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(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY));
|
||||
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(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendErrorCausesEarlySaveOfContext() throws Exception {
|
||||
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();
|
||||
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(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY));
|
||||
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(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noSessionIsCreatedIfSessionWasInvalidatedDuringTheRequest() throws Exception {
|
||||
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.getSession();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
|
||||
SecurityContextHolder.setContext(repo.loadContext(holder));
|
||||
SecurityContextHolder.getContext().setAuthentication(testToken);
|
||||
request.getSession().invalidate();
|
||||
repo.saveContext(SecurityContextHolder.getContext(), holder.getRequest(), holder.getResponse());
|
||||
assertNull(request.getSession(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void settingCloneFromContextLoadsClonedContextObject() throws Exception {
|
||||
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();
|
||||
repo.setCloneFromHttpSession(true);
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockContext contextBefore = new MockContext();
|
||||
request.getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, contextBefore);
|
||||
contextBefore.setAuthentication(testToken);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
|
||||
SecurityContext loadedContext = repo.loadContext(holder);
|
||||
assertTrue(loadedContext instanceof MockContext);
|
||||
assertFalse(loadedContext == contextBefore);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generateNewContextWorksWithContextClass() throws Exception {
|
||||
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();
|
||||
repo.setSecurityContextClass(MockContext.class);
|
||||
assertTrue(repo.generateNewContext() instanceof MockContext);
|
||||
}
|
||||
|
||||
static class MockContext implements Cloneable, SecurityContext {
|
||||
Authentication a;
|
||||
|
||||
public Authentication getAuthentication() {
|
||||
return a;
|
||||
}
|
||||
|
||||
public void setAuthentication(Authentication authentication) {
|
||||
a = authentication;
|
||||
}
|
||||
|
||||
public Object clone() {
|
||||
MockContext mc = new MockContext();
|
||||
mc.setAuthentication(this.getAuthentication());
|
||||
return mc;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
package org.springframework.security.context;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
import org.jmock.integration.junit4.JUnit4Mockery;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.providers.TestingAuthenticationToken;
|
||||
import org.springframework.security.ui.FilterChainOrder;
|
||||
|
||||
public class SecurityContextPersistenceFilterTests {
|
||||
Mockery jmock = new JUnit4Mockery();
|
||||
TestingAuthenticationToken testToken = new TestingAuthenticationToken("someone", "passwd", "ROLE_A");
|
||||
|
||||
@After
|
||||
public void clearContext() {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contextIsClearedAfterChainProceeds() throws Exception {
|
||||
final FilterChain chain = jmock.mock(FilterChain.class);
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
SecurityContextPersistenceFilter filter = new SecurityContextPersistenceFilter();
|
||||
SecurityContextHolder.getContext().setAuthentication(testToken);
|
||||
jmock.checking(new Expectations() {{
|
||||
oneOf(chain).doFilter(with(aNonNull(HttpServletRequest.class)), with(aNonNull(HttpServletResponse.class)));
|
||||
}});
|
||||
|
||||
filter.doFilter(request, response, chain);
|
||||
assertNull(SecurityContextHolder.getContext().getAuthentication());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contextIsStillClearedIfExceptionIsThrowByFilterChain() throws Exception {
|
||||
final FilterChain chain = jmock.mock(FilterChain.class);
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
SecurityContextPersistenceFilter filter = new SecurityContextPersistenceFilter();
|
||||
SecurityContextHolder.getContext().setAuthentication(testToken);
|
||||
|
||||
jmock.checking(new Expectations() {{
|
||||
oneOf(chain).doFilter(with(aNonNull(HttpServletRequest.class)), with(aNonNull(HttpServletResponse.class)));
|
||||
will(throwException(new IOException()));
|
||||
}});
|
||||
try {
|
||||
filter.doFilter(request, response, chain);
|
||||
fail();
|
||||
} catch(IOException expected) {
|
||||
}
|
||||
|
||||
assertNull(SecurityContextHolder.getContext().getAuthentication());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadedContextContextIsCopiedToSecurityContextHolderAndUpdatedContextIsStored() throws Exception {
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
SecurityContextPersistenceFilter filter = new SecurityContextPersistenceFilter();
|
||||
final TestingAuthenticationToken beforeAuth = new TestingAuthenticationToken("someoneelse", "passwd", "ROLE_B");
|
||||
final SecurityContext scBefore = new SecurityContextImpl();
|
||||
final SecurityContext scExpectedAfter = new SecurityContextImpl();
|
||||
scExpectedAfter.setAuthentication(testToken);
|
||||
scBefore.setAuthentication(beforeAuth);
|
||||
final SecurityContextRepository repo = jmock.mock(SecurityContextRepository.class);
|
||||
filter.setSecurityContextRepository(repo);
|
||||
|
||||
jmock.checking(new Expectations() {{
|
||||
oneOf(repo).loadContext(with(aNonNull(HttpRequestResponseHolder.class))); will(returnValue(scBefore));
|
||||
oneOf(repo).saveContext(scExpectedAfter, request, response);
|
||||
}});
|
||||
|
||||
final FilterChain chain = new FilterChain() {
|
||||
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
|
||||
assertEquals(beforeAuth, SecurityContextHolder.getContext().getAuthentication());
|
||||
// Change the context here
|
||||
SecurityContextHolder.setContext(scExpectedAfter);
|
||||
}
|
||||
};
|
||||
|
||||
filter.doFilter(request, response, chain);
|
||||
|
||||
jmock.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterIsOnlyAppliedOncePerRequest() throws Exception {
|
||||
final FilterChain chain = jmock.mock(FilterChain.class);
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
SecurityContextPersistenceFilter filter = new SecurityContextPersistenceFilter();
|
||||
final SecurityContextRepository repo = jmock.mock(SecurityContextRepository.class);
|
||||
filter.setSecurityContextRepository(repo);
|
||||
final SecurityContext sc = SecurityContextHolder.getContext();
|
||||
|
||||
jmock.checking(new Expectations() {{
|
||||
oneOf(repo).loadContext(with(aNonNull(HttpRequestResponseHolder.class))); will(returnValue(sc));
|
||||
oneOf(repo).saveContext(sc, request, response);
|
||||
exactly(2).of(chain).doFilter(request, response);
|
||||
}});
|
||||
|
||||
filter.doFilter(request, response, chain);
|
||||
assertNotNull(request.getAttribute(SecurityContextPersistenceFilter.FILTER_APPLIED));
|
||||
filter.doFilter(request, response, chain);
|
||||
jmock.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sessionIsEagerlyCreatedWhenConfigured() throws Exception {
|
||||
final FilterChain chain = jmock.mock(FilterChain.class);
|
||||
jmock.checking(new Expectations() {{ ignoring(chain); }});
|
||||
final MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
SecurityContextPersistenceFilter filter = new SecurityContextPersistenceFilter();
|
||||
filter.setForceEagerSessionCreation(true);
|
||||
filter.doFilter(request, response, chain);
|
||||
assertNotNull(request.getSession(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterOrderHasExpectedValue() throws Exception {
|
||||
assertEquals(FilterChainOrder.SECURITY_CONTEXT_FILTER, (new SecurityContextPersistenceFilter()).getOrder());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue