SEC-1229: Added support for parsing error URL in session-management
This commit is contained in:
parent
203cc5a8dc
commit
ebada9fd12
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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.FilterSecurityInterceptor;
|
||||
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.context.HttpSessionSecurityContextRepository;
|
||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
|
||||
|
@ -192,7 +193,7 @@ class HttpConfigurationBuilder {
|
|||
|
||||
void createSessionManagementFilters() {
|
||||
Element sessionMgmtElt = DomUtils.getChildElementByTagName(httpElt, Elements.SESSION_MANAGEMENT);
|
||||
Element sessionCtrlEt = null;
|
||||
Element sessionCtrlElt = null;
|
||||
|
||||
String sessionFixationAttribute = null;
|
||||
String invalidSessionUrl = null;
|
||||
|
@ -200,10 +201,10 @@ class HttpConfigurationBuilder {
|
|||
if (sessionMgmtElt != null) {
|
||||
sessionFixationAttribute = sessionMgmtElt.getAttribute(ATT_SESSION_FIXATION_PROTECTION);
|
||||
invalidSessionUrl = sessionMgmtElt.getAttribute(ATT_INVALID_SESSION_URL);
|
||||
sessionCtrlEt = DomUtils.getChildElementByTagName(sessionMgmtElt, Elements.CONCURRENT_SESSIONS);
|
||||
sessionCtrlElt = DomUtils.getChildElementByTagName(sessionMgmtElt, Elements.CONCURRENT_SESSIONS);
|
||||
|
||||
if (sessionCtrlEt != null) {
|
||||
createConcurrencyControlFilterAndSessionRegistry(sessionCtrlEt);
|
||||
if (sessionCtrlElt != null) {
|
||||
createConcurrencyControlFilterAndSessionRegistry(sessionCtrlElt);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -214,22 +215,25 @@ class HttpConfigurationBuilder {
|
|||
boolean sessionFixationProtectionRequired = !sessionFixationAttribute.equals(OPT_SESSION_FIXATION_NO_PROTECTION);
|
||||
|
||||
BeanDefinitionBuilder sessionStrategy;
|
||||
String concurrencyErrorUrl = null;
|
||||
|
||||
if (sessionCtrlEt != null) {
|
||||
if (sessionCtrlElt != null) {
|
||||
assert sessionRegistryRef != null;
|
||||
sessionStrategy = BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionControlAuthenticatedSessionStrategy.class);
|
||||
sessionStrategy.addConstructorArgValue(sessionRegistryRef);
|
||||
|
||||
String maxSessions = sessionCtrlEt.getAttribute("max-sessions");
|
||||
String maxSessions = sessionCtrlElt.getAttribute("max-sessions");
|
||||
|
||||
if (StringUtils.hasText(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)) {
|
||||
sessionStrategy.addPropertyValue("exceptionIfMaximumExceeded", exceptionIfMaximumExceeded);
|
||||
|
||||
concurrencyErrorUrl = sessionCtrlElt.getAttribute("error-url");
|
||||
}
|
||||
} else if (sessionFixationProtectionRequired || StringUtils.hasText(invalidSessionUrl)) {
|
||||
sessionStrategy = BeanDefinitionBuilder.rootBeanDefinition(DefaultSessionAuthenticationStrategy.class);
|
||||
|
@ -239,6 +243,11 @@ class HttpConfigurationBuilder {
|
|||
}
|
||||
|
||||
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);
|
||||
BeanDefinition strategyBean = sessionStrategy.getBeanDefinition();
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ import org.springframework.security.access.ConfigAttribute;
|
|||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.authentication.concurrent.ConcurrentLoginException;
|
||||
import org.springframework.security.authentication.concurrent.SessionRegistryImpl;
|
||||
import org.springframework.security.config.BeanIds;
|
||||
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.savedrequest.HttpSessionRequestCache;
|
||||
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.wrapper.SecurityContextHolderAwareRequestFilter;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
@ -704,33 +702,27 @@ public class HttpSecurityBeanDefinitionParserTests {
|
|||
assertSame(sessionRegistry, sessionRegistryFromFormLoginFilter);
|
||||
}
|
||||
|
||||
@Test(expected=ConcurrentLoginException.class)
|
||||
@Test
|
||||
public void concurrentSessionMaxSessionsIsCorrectlyConfigured() throws Exception {
|
||||
setContext(
|
||||
"<http auto-config='true'>" +
|
||||
" <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>" +
|
||||
"</http>" + AUTH_PROVIDER_XML);
|
||||
SessionAuthenticationStrategy seshStrategy = (SessionAuthenticationStrategy) FieldUtils.getFieldValue(
|
||||
getFilter(SessionManagementFilter.class), "sessionStrategy");
|
||||
SessionManagementFilter seshStrategy = (SessionManagementFilter) getFilter(SessionManagementFilter.class);
|
||||
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("bob", "pass");
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
// Register 2 sessions and then check a third
|
||||
// req.setSession(new MockHttpSession());
|
||||
// auth.setDetails(new WebAuthenticationDetails(req));
|
||||
try {
|
||||
seshStrategy.onAuthentication(auth, new MockHttpServletRequest(), new MockHttpServletResponse());
|
||||
} catch (ConcurrentLoginException e) {
|
||||
fail("First login should be allowed");
|
||||
}
|
||||
|
||||
try {
|
||||
seshStrategy.onAuthentication(auth, new MockHttpServletRequest(), new MockHttpServletResponse());
|
||||
} catch (ConcurrentLoginException e) {
|
||||
fail("Second login should be allowed");
|
||||
}
|
||||
|
||||
seshStrategy.onAuthentication(auth, new MockHttpServletRequest(), new MockHttpServletResponse());
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
seshStrategy.doFilter(new MockHttpServletRequest(), response, new MockFilterChain());
|
||||
assertNull(response.getRedirectedUrl());
|
||||
seshStrategy.doFilter(new MockHttpServletRequest(), response, new MockFilterChain());
|
||||
assertNull(response.getRedirectedUrl());
|
||||
seshStrategy.doFilter(new MockHttpServletRequest(), response, new MockFilterChain());
|
||||
assertEquals("/max-exceeded", response.getRedirectedUrl());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in New Issue