SEC-1396: Implement eager saving of SecurityContext in SessionManagementFilter on authentication.

The user is then seen as being authenticated to further (re-entrant) requests which occur before the existing request has completed. The saving logic is contained with the SecurityContextRepository implementation.
This commit is contained in:
Luke Taylor 2010-02-11 17:47:22 +00:00
parent 403f8da79a
commit dcbdfc2026
4 changed files with 17 additions and 8 deletions

View File

@ -36,6 +36,7 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
import org.springframework.security.config.BeanIds; import org.springframework.security.config.BeanIds;
import org.springframework.security.config.PostProcessedMockUserDetailsService; import org.springframework.security.config.PostProcessedMockUserDetailsService;
import org.springframework.security.config.util.InMemoryXmlApplicationContext; import org.springframework.security.config.util.InMemoryXmlApplicationContext;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.session.SessionRegistryImpl; import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.openid.OpenID4JavaConsumer; import org.springframework.security.openid.OpenID4JavaConsumer;
@ -77,6 +78,7 @@ import org.springframework.security.web.authentication.rememberme.TokenBasedReme
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter; import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository; import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.context.SaveContextOnUpdateOrErrorResponseWrapper;
import org.springframework.security.web.context.SecurityContextPersistenceFilter; import org.springframework.security.web.context.SecurityContextPersistenceFilter;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache; import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter; import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
@ -790,13 +792,17 @@ public class HttpSecurityBeanDefinitionParserTests {
// Register 2 sessions and then check a third // Register 2 sessions and then check a third
// req.setSession(new MockHttpSession()); // req.setSession(new MockHttpSession());
// auth.setDetails(new WebAuthenticationDetails(req)); // auth.setDetails(new WebAuthenticationDetails(req));
MockHttpServletResponse response = new MockHttpServletResponse(); MockHttpServletResponse mockResponse = new MockHttpServletResponse();
SaveContextOnUpdateOrErrorResponseWrapper response = new SaveContextOnUpdateOrErrorResponseWrapper(mockResponse, false) {
protected void saveContext(SecurityContext context) {
}
};
seshFilter.doFilter(new MockHttpServletRequest(), response, new MockFilterChain()); seshFilter.doFilter(new MockHttpServletRequest(), response, new MockFilterChain());
assertNull(response.getRedirectedUrl()); assertNull(mockResponse.getRedirectedUrl());
seshFilter.doFilter(new MockHttpServletRequest(), response, new MockFilterChain()); seshFilter.doFilter(new MockHttpServletRequest(), response, new MockFilterChain());
assertNull(response.getRedirectedUrl()); assertNull(mockResponse.getRedirectedUrl());
seshFilter.doFilter(new MockHttpServletRequest(), response, new MockFilterChain()); seshFilter.doFilter(new MockHttpServletRequest(), response, new MockFilterChain());
assertEquals("/max-exceeded", response.getRedirectedUrl()); assertEquals("/max-exceeded", mockResponse.getRedirectedUrl());
} }
@Test @Test

View File

@ -98,7 +98,7 @@ public class HttpSessionSecurityContextRepository implements SecurityContextRepo
} }
public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) { public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {
SaveToSessionResponseWrapper responseWrapper = (SaveToSessionResponseWrapper)response; SaveContextOnUpdateOrErrorResponseWrapper responseWrapper = (SaveContextOnUpdateOrErrorResponseWrapper)response;
// saveContext() might already be called by the response wrapper // saveContext() might already be called by the response wrapper
// if something in the chain called sendError() or sendRedirect(). This ensures we only call it // if something in the chain called sendError() or sendRedirect(). This ensures we only call it
// once per request. // once per request.
@ -289,7 +289,7 @@ public class HttpSessionSecurityContextRepository implements SecurityContextRepo
* Stores the necessary state from the start of the request in order to make a decision about whether * 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. * the security context has changed before saving it.
*/ */
class SaveToSessionResponseWrapper extends SaveContextOnUpdateOrErrorResponseWrapper { final class SaveToSessionResponseWrapper extends SaveContextOnUpdateOrErrorResponseWrapper {
private HttpServletRequest request; private HttpServletRequest request;
private boolean httpSessionExistedAtStartOfRequest; private boolean httpSessionExistedAtStartOfRequest;
@ -327,7 +327,7 @@ public class HttpSessionSecurityContextRepository implements SecurityContextRepo
* *
*/ */
@Override @Override
void saveContext(SecurityContext context) { protected void saveContext(SecurityContext context) {
// See SEC-776 // See SEC-776
if (authenticationTrustResolver.isAnonymous(context.getAuthentication())) { if (authenticationTrustResolver.isAnonymous(context.getAuthentication())) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {

View File

@ -42,7 +42,7 @@ public abstract class SaveContextOnUpdateOrErrorResponseWrapper extends HttpServ
* *
* @param context the <tt>SecurityContext</tt> instance to store * @param context the <tt>SecurityContext</tt> instance to store
*/ */
abstract void saveContext(SecurityContext context); protected abstract void saveContext(SecurityContext context);
/** /**
* Makes sure the session is updated before calling the * Makes sure the session is updated before calling the

View File

@ -78,6 +78,9 @@ public class SessionManagementFilter extends GenericFilterBean {
return; return;
} }
// Eagerly save the security context to make it available for any possible re-entrant
// requests which may occur before the current request completes. SEC-1396.
securityContextRepository.saveContext(SecurityContextHolder.getContext(), request, response);
} else { } else {
// No security context or authentication present. Check for a session timeout // No security context or authentication present. Check for a session timeout
if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) { if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) {