SEC-1229: Added support for parsing error URL in session-management

This commit is contained in:
Luke Taylor 2009-09-29 16:17:05 +00:00
parent 203cc5a8dc
commit ebada9fd12
3 changed files with 27 additions and 98 deletions

View File

@ -1,72 +0,0 @@
package org.springframework.security.config.http;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.concurrent.ConcurrentSessionControllerImpl;
import org.springframework.security.authentication.concurrent.SessionRegistryImpl;
import org.springframework.security.web.authentication.concurrent.ConcurrentSessionFilter;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* Sets up support for concurrent session support control, creating {@link ConcurrentSessionFilter},
* {@link SessionRegistryImpl} and {@link ConcurrentSessionControllerImpl}. The session controller is also registered
* with the default {@link ProviderManager} (which is automatically registered during namespace configuration).
*
* @author Luke Taylor
* @version $Id$
*/
public class ConcurrentSessionsBeanDefinitionParser implements BeanDefinitionParser {
static final String ATT_EXPIRY_URL = "expired-url";
static final String ATT_SESSION_REGISTRY_ALIAS = "session-registry-alias";
static final String ATT_SESSION_REGISTRY_REF = "session-registry-ref";
public BeanDefinition parse(Element element, ParserContext pc) {
CompositeComponentDefinition compositeDef =
new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element));
pc.pushContainingComponent(compositeDef);
BeanDefinitionRegistry beanRegistry = pc.getRegistry();
String sessionRegistryId = element.getAttribute(ATT_SESSION_REGISTRY_REF);
if (!StringUtils.hasText(sessionRegistryId)) {
// Register an internal SessionRegistryImpl if no external reference supplied.
RootBeanDefinition sessionRegistry = new RootBeanDefinition(SessionRegistryImpl.class);
sessionRegistryId = pc.getReaderContext().registerWithGeneratedName(sessionRegistry);
pc.registerComponent(new BeanComponentDefinition(sessionRegistry, sessionRegistryId));
}
String registryAlias = element.getAttribute(ATT_SESSION_REGISTRY_ALIAS);
if (StringUtils.hasText(registryAlias)) {
beanRegistry.registerAlias(sessionRegistryId, registryAlias);
}
BeanDefinitionBuilder filterBuilder =
BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionFilter.class);
filterBuilder.addPropertyReference("sessionRegistry", sessionRegistryId);
Object source = pc.extractSource(element);
filterBuilder.getRawBeanDefinition().setSource(source);
filterBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
String expiryUrl = element.getAttribute(ATT_EXPIRY_URL);
if (StringUtils.hasText(expiryUrl)) {
WebConfigUtils.validateHttpRedirect(expiryUrl, pc, source);
filterBuilder.addPropertyValue("expiredUrl", expiryUrl);
}
pc.popAndRegisterContainingComponent();
return filterBuilder.getBeanDefinition();
}
}

View File

@ -37,6 +37,7 @@ import org.springframework.security.web.access.expression.WebExpressionVoter;
import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource; import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.access.intercept.RequestKey; import org.springframework.security.web.access.intercept.RequestKey;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.concurrent.ConcurrentSessionFilter; import org.springframework.security.web.authentication.concurrent.ConcurrentSessionFilter;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository; import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextPersistenceFilter; import org.springframework.security.web.context.SecurityContextPersistenceFilter;
@ -192,7 +193,7 @@ class HttpConfigurationBuilder {
void createSessionManagementFilters() { void createSessionManagementFilters() {
Element sessionMgmtElt = DomUtils.getChildElementByTagName(httpElt, Elements.SESSION_MANAGEMENT); Element sessionMgmtElt = DomUtils.getChildElementByTagName(httpElt, Elements.SESSION_MANAGEMENT);
Element sessionCtrlEt = null; Element sessionCtrlElt = null;
String sessionFixationAttribute = null; String sessionFixationAttribute = null;
String invalidSessionUrl = null; String invalidSessionUrl = null;
@ -200,10 +201,10 @@ class HttpConfigurationBuilder {
if (sessionMgmtElt != null) { if (sessionMgmtElt != null) {
sessionFixationAttribute = sessionMgmtElt.getAttribute(ATT_SESSION_FIXATION_PROTECTION); sessionFixationAttribute = sessionMgmtElt.getAttribute(ATT_SESSION_FIXATION_PROTECTION);
invalidSessionUrl = sessionMgmtElt.getAttribute(ATT_INVALID_SESSION_URL); invalidSessionUrl = sessionMgmtElt.getAttribute(ATT_INVALID_SESSION_URL);
sessionCtrlEt = DomUtils.getChildElementByTagName(sessionMgmtElt, Elements.CONCURRENT_SESSIONS); sessionCtrlElt = DomUtils.getChildElementByTagName(sessionMgmtElt, Elements.CONCURRENT_SESSIONS);
if (sessionCtrlEt != null) { if (sessionCtrlElt != null) {
createConcurrencyControlFilterAndSessionRegistry(sessionCtrlEt); createConcurrencyControlFilterAndSessionRegistry(sessionCtrlElt);
} }
} }
@ -214,22 +215,25 @@ class HttpConfigurationBuilder {
boolean sessionFixationProtectionRequired = !sessionFixationAttribute.equals(OPT_SESSION_FIXATION_NO_PROTECTION); boolean sessionFixationProtectionRequired = !sessionFixationAttribute.equals(OPT_SESSION_FIXATION_NO_PROTECTION);
BeanDefinitionBuilder sessionStrategy; BeanDefinitionBuilder sessionStrategy;
String concurrencyErrorUrl = null;
if (sessionCtrlEt != null) { if (sessionCtrlElt != null) {
assert sessionRegistryRef != null; assert sessionRegistryRef != null;
sessionStrategy = BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionControlAuthenticatedSessionStrategy.class); sessionStrategy = BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionControlAuthenticatedSessionStrategy.class);
sessionStrategy.addConstructorArgValue(sessionRegistryRef); sessionStrategy.addConstructorArgValue(sessionRegistryRef);
String maxSessions = sessionCtrlEt.getAttribute("max-sessions"); String maxSessions = sessionCtrlElt.getAttribute("max-sessions");
if (StringUtils.hasText(maxSessions)) { if (StringUtils.hasText(maxSessions)) {
sessionStrategy.addPropertyValue("maximumSessions", maxSessions); sessionStrategy.addPropertyValue("maximumSessions", maxSessions);
} }
String exceptionIfMaximumExceeded = sessionCtrlEt.getAttribute("exception-if-maximum-exceeded"); String exceptionIfMaximumExceeded = sessionCtrlElt.getAttribute("error-if-maximum-exceeded");
if (StringUtils.hasText(exceptionIfMaximumExceeded)) { if (StringUtils.hasText(exceptionIfMaximumExceeded)) {
sessionStrategy.addPropertyValue("exceptionIfMaximumExceeded", exceptionIfMaximumExceeded); sessionStrategy.addPropertyValue("exceptionIfMaximumExceeded", exceptionIfMaximumExceeded);
concurrencyErrorUrl = sessionCtrlElt.getAttribute("error-url");
} }
} else if (sessionFixationProtectionRequired || StringUtils.hasText(invalidSessionUrl)) { } else if (sessionFixationProtectionRequired || StringUtils.hasText(invalidSessionUrl)) {
sessionStrategy = BeanDefinitionBuilder.rootBeanDefinition(DefaultSessionAuthenticationStrategy.class); sessionStrategy = BeanDefinitionBuilder.rootBeanDefinition(DefaultSessionAuthenticationStrategy.class);
@ -239,6 +243,11 @@ class HttpConfigurationBuilder {
} }
BeanDefinitionBuilder sessionMgmtFilter = BeanDefinitionBuilder.rootBeanDefinition(SessionManagementFilter.class); BeanDefinitionBuilder sessionMgmtFilter = BeanDefinitionBuilder.rootBeanDefinition(SessionManagementFilter.class);
RootBeanDefinition failureHandler = new RootBeanDefinition(SimpleUrlAuthenticationFailureHandler.class);
if (StringUtils.hasText(concurrencyErrorUrl)) {
failureHandler.getPropertyValues().addPropertyValue("defaultFailureUrl", concurrencyErrorUrl);
}
sessionMgmtFilter.addPropertyValue("authenticationFailureHandler", failureHandler);
sessionMgmtFilter.addConstructorArgValue(contextRepoRef); sessionMgmtFilter.addConstructorArgValue(contextRepoRef);
BeanDefinition strategyBean = sessionStrategy.getBeanDefinition(); BeanDefinition strategyBean = sessionStrategy.getBeanDefinition();

View File

@ -27,7 +27,6 @@ import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig; import org.springframework.security.access.SecurityConfig;
import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.concurrent.ConcurrentLoginException;
import org.springframework.security.authentication.concurrent.SessionRegistryImpl; import org.springframework.security.authentication.concurrent.SessionRegistryImpl;
import org.springframework.security.config.BeanIds; import org.springframework.security.config.BeanIds;
import org.springframework.security.config.PostProcessedMockUserDetailsService; import org.springframework.security.config.PostProcessedMockUserDetailsService;
@ -71,7 +70,6 @@ import org.springframework.security.web.context.HttpSessionSecurityContextReposi
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;
import org.springframework.security.web.session.SessionAuthenticationStrategy;
import org.springframework.security.web.session.SessionManagementFilter; import org.springframework.security.web.session.SessionManagementFilter;
import org.springframework.security.web.wrapper.SecurityContextHolderAwareRequestFilter; import org.springframework.security.web.wrapper.SecurityContextHolderAwareRequestFilter;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
@ -704,33 +702,27 @@ public class HttpSecurityBeanDefinitionParserTests {
assertSame(sessionRegistry, sessionRegistryFromFormLoginFilter); assertSame(sessionRegistry, sessionRegistryFromFormLoginFilter);
} }
@Test(expected=ConcurrentLoginException.class) @Test
public void concurrentSessionMaxSessionsIsCorrectlyConfigured() throws Exception { public void concurrentSessionMaxSessionsIsCorrectlyConfigured() throws Exception {
setContext( setContext(
"<http auto-config='true'>" + "<http auto-config='true'>" +
" <session-management>" + " <session-management>" +
" <concurrency-control max-sessions='2' exception-if-maximum-exceeded='true' />" + " <concurrency-control max-sessions='2' error-if-maximum-exceeded='true' error-url='/max-exceeded' />" +
" </session-management>" + " </session-management>" +
"</http>" + AUTH_PROVIDER_XML); "</http>" + AUTH_PROVIDER_XML);
SessionAuthenticationStrategy seshStrategy = (SessionAuthenticationStrategy) FieldUtils.getFieldValue( SessionManagementFilter seshStrategy = (SessionManagementFilter) getFilter(SessionManagementFilter.class);
getFilter(SessionManagementFilter.class), "sessionStrategy");
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("bob", "pass"); UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("bob", "pass");
SecurityContextHolder.getContext().setAuthentication(auth);
// 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));
try { MockHttpServletResponse response = new MockHttpServletResponse();
seshStrategy.onAuthentication(auth, new MockHttpServletRequest(), new MockHttpServletResponse()); seshStrategy.doFilter(new MockHttpServletRequest(), response, new MockFilterChain());
} catch (ConcurrentLoginException e) { assertNull(response.getRedirectedUrl());
fail("First login should be allowed"); seshStrategy.doFilter(new MockHttpServletRequest(), response, new MockFilterChain());
} assertNull(response.getRedirectedUrl());
seshStrategy.doFilter(new MockHttpServletRequest(), response, new MockFilterChain());
try { assertEquals("/max-exceeded", response.getRedirectedUrl());
seshStrategy.onAuthentication(auth, new MockHttpServletRequest(), new MockHttpServletResponse());
} catch (ConcurrentLoginException e) {
fail("Second login should be allowed");
}
seshStrategy.onAuthentication(auth, new MockHttpServletRequest(), new MockHttpServletResponse());
} }
@Test @Test