1
0
mirror of synced 2026-08-07 10:47:49 +00:00

SEC-1229: Redesign Concurrent Session Control implementation. Added ConcurrentSessionControlAuthenticatedSessionStrategy

This commit is contained in:
Luke Taylor
2009-08-27 10:43:01 +00:00
parent ab0d66071a
commit 471206a29d
13 changed files with 377 additions and 198 deletions
@@ -63,6 +63,7 @@ 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.ConcurrentSessionControlAuthenticatedSessionStrategy;
import org.springframework.security.web.session.DefaultAuthenticatedSessionStrategy;
import org.springframework.security.web.session.SessionManagementFilter;
import org.springframework.security.web.util.AntUrlPathMatcher;
@@ -180,8 +181,8 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
BeanDefinition cpf = null;
BeanReference sessionRegistryRef = null;
BeanReference concurrentSessionControllerRef = null;
BeanDefinition concurrentSessionFilter = createConcurrentSessionFilterAndRelatedBeansIfRequired(element, pc);
// BeanReference concurrentSessionControllerRef = null;
BeanDefinition concurrentSessionFilter = createConcurrentSessionFilter(element, pc);
BeanDefinition scpf = createSecurityContextPersistenceFilter(element, pc);
BeanReference contextRepoRef = (BeanReference) scpf.getPropertyValues().getPropertyValue("securityContextRepository").getValue();
@@ -189,13 +190,13 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
if (concurrentSessionFilter != null) {
sessionRegistryRef = (BeanReference)
concurrentSessionFilter.getPropertyValues().getPropertyValue("sessionRegistry").getValue();
logger.info("Concurrent session filter in use, setting 'forceEagerSessionCreation' to true");
scpf.getPropertyValues().addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
concurrentSessionControllerRef = createConcurrentSessionController(element, concurrentSessionFilter, sessionRegistryRef, pc);
// logger.info("Concurrent session filter in use, setting 'forceEagerSessionCreation' to true");
// scpf.getPropertyValues().addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
// concurrentSessionControllerRef = createConcurrentSessionController(element, concurrentSessionFilter, sessionRegistryRef, pc);
}
ManagedList<BeanReference> authenticationProviders = new ManagedList<BeanReference>();
BeanReference authenticationManager = createAuthenticationManager(element, pc, authenticationProviders, concurrentSessionControllerRef);
BeanReference authenticationManager = createAuthenticationManager(element, pc, authenticationProviders, null);
BeanDefinition servApiFilter = createServletApiFilter(element, pc);
// Register the portMapper. A default will always be created, even if no element exists.
@@ -715,7 +716,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
return null;
}
private BeanDefinition createConcurrentSessionFilterAndRelatedBeansIfRequired(Element element, ParserContext parserContext) {
private BeanDefinition createConcurrentSessionFilter(Element element, ParserContext parserContext) {
Element sessionControlElt = DomUtils.getChildElementByTagName(element, Elements.CONCURRENT_SESSIONS);
if (sessionControlElt == null) {
return null;
@@ -729,16 +730,16 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
Element sessionCtrlElement = DomUtils.getChildElementByTagName(elt, Elements.CONCURRENT_SESSIONS);
// Check for a custom controller
String sessionControllerRef = sessionCtrlElement.getAttribute(ATT_SESSION_CONTROLLER_REF);
if (StringUtils.hasText(sessionControllerRef)) {
if (!StringUtils.hasText(sessionCtrlElement.getAttribute(ConcurrentSessionsBeanDefinitionParser.ATT_SESSION_REGISTRY_REF))) {
pc.getReaderContext().error("Use of " + ATT_SESSION_CONTROLLER_REF + " requires that " +
ConcurrentSessionsBeanDefinitionParser.ATT_SESSION_REGISTRY_REF + " is also set.",
pc.extractSource(sessionCtrlElement));
}
return new RuntimeBeanReference(sessionControllerRef);
}
// String sessionControllerRef = sessionCtrlElement.getAttribute(ATT_SESSION_CONTROLLER_REF);
//
// if (StringUtils.hasText(sessionControllerRef)) {
// if (!StringUtils.hasText(sessionCtrlElement.getAttribute(ConcurrentSessionsBeanDefinitionParser.ATT_SESSION_REGISTRY_REF))) {
// pc.getReaderContext().error("Use of " + ATT_SESSION_CONTROLLER_REF + " requires that " +
// ConcurrentSessionsBeanDefinitionParser.ATT_SESSION_REGISTRY_REF + " is also set.",
// pc.extractSource(sessionCtrlElement));
// }
// return new RuntimeBeanReference(sessionControllerRef);
// }
BeanDefinitionBuilder controllerBuilder = BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionControllerImpl.class);
controllerBuilder.getRawBeanDefinition().setSource(filter.getSource());
@@ -918,6 +919,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
private RootBeanDefinition createSessionManagementFilter(Element elt, ParserContext pc,
BeanReference sessionRegistryRef, BeanReference contextRepoRef) {
Element sessionCtrlElement = DomUtils.getChildElementByTagName(elt, Elements.CONCURRENT_SESSIONS);
String sessionFixationAttribute = elt.getAttribute(ATT_SESSION_FIXATION_PROTECTION);
String invalidSessionUrl = elt.getAttribute(ATT_INVALID_SESSION_URL);
@@ -927,35 +929,48 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
boolean sessionFixationProtectionRequired = !sessionFixationAttribute.equals(OPT_SESSION_FIXATION_NO_PROTECTION);
if (sessionFixationProtectionRequired || StringUtils.hasText(invalidSessionUrl)) {
BeanDefinitionBuilder sessionFixationFilter =
BeanDefinitionBuilder.rootBeanDefinition(SessionManagementFilter.class);
sessionFixationFilter.addConstructorArgValue(contextRepoRef);
BeanDefinitionBuilder sessionStrategy;
if (sessionFixationProtectionRequired) {
BeanDefinitionBuilder sessionStrategy = BeanDefinitionBuilder.rootBeanDefinition(DefaultAuthenticatedSessionStrategy.class);
if (sessionCtrlElement != null) {
assert sessionRegistryRef != null;
sessionStrategy = BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionControlAuthenticatedSessionStrategy.class);
sessionStrategy.addConstructorArgValue(sessionRegistryRef);
sessionStrategy.addPropertyValue("migrateSessionAttributes",
Boolean.valueOf(sessionFixationAttribute.equals(OPT_SESSION_FIXATION_MIGRATE_SESSION)));
if (sessionRegistryRef != null) {
sessionStrategy.addPropertyValue("sessionRegistry", sessionRegistryRef);
}
BeanDefinition strategyBean = sessionStrategy.getBeanDefinition();
String id = pc.getReaderContext().registerWithGeneratedName(strategyBean);
pc.registerBeanComponent(new BeanComponentDefinition(strategyBean, id));
sessionFixationFilter.addPropertyReference("authenticatedSessionStrategy", id);
String maxSessions = sessionCtrlElement.getAttribute("max-sessions");
if (StringUtils.hasText(maxSessions)) {
sessionStrategy.addPropertyValue("maximumSessions", maxSessions);
}
if (StringUtils.hasText(invalidSessionUrl)) {
sessionFixationFilter.addPropertyValue("invalidSessionUrl", invalidSessionUrl);
}
String exceptionIfMaximumExceeded = sessionCtrlElement.getAttribute("exception-if-maximum-exceeded");
return (RootBeanDefinition) sessionFixationFilter.getBeanDefinition();
if (StringUtils.hasText(exceptionIfMaximumExceeded)) {
sessionStrategy.addPropertyValue("exceptionIfMaximumExceeded", exceptionIfMaximumExceeded);
}
} else if (sessionFixationProtectionRequired || StringUtils.hasText(invalidSessionUrl)) {
sessionStrategy = BeanDefinitionBuilder.rootBeanDefinition(DefaultAuthenticatedSessionStrategy.class);
} else {
return null;
}
return null;
BeanDefinitionBuilder sessionMgmtFilter = BeanDefinitionBuilder.rootBeanDefinition(SessionManagementFilter.class);
sessionMgmtFilter.addConstructorArgValue(contextRepoRef);
BeanDefinition strategyBean = sessionStrategy.getBeanDefinition();
String id = pc.getReaderContext().registerWithGeneratedName(strategyBean);
pc.registerBeanComponent(new BeanComponentDefinition(strategyBean, id));
sessionMgmtFilter.addPropertyReference("authenticatedSessionStrategy", id);
if (sessionFixationProtectionRequired) {
sessionStrategy.addPropertyValue("migrateSessionAttributes",
Boolean.valueOf(sessionFixationAttribute.equals(OPT_SESSION_FIXATION_MIGRATE_SESSION)));
}
if (StringUtils.hasText(invalidSessionUrl)) {
sessionMgmtFilter.addPropertyValue("invalidSessionUrl", invalidSessionUrl);
}
return (RootBeanDefinition) sessionMgmtFilter.getBeanDefinition();
}
private FilterAndEntryPoint createFormLoginFilter(Element element, ParserContext pc, boolean autoConfig,
@@ -22,14 +22,12 @@ import org.springframework.context.support.AbstractXmlApplicationContext;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.security.access.AccessDeniedException;
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.ConcurrentSessionController;
import org.springframework.security.authentication.concurrent.ConcurrentSessionControllerImpl;
import org.springframework.security.authentication.concurrent.SessionRegistryImpl;
import org.springframework.security.config.BeanIds;
@@ -59,7 +57,6 @@ import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.security.web.authentication.concurrent.ConcurrentSessionFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.logout.LogoutHandler;
@@ -74,6 +71,7 @@ import org.springframework.security.web.authentication.www.BasicProcessingFilter
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
import org.springframework.security.web.session.AuthenticatedSessionStrategy;
import org.springframework.security.web.session.SessionManagementFilter;
import org.springframework.security.web.wrapper.SecurityContextHolderAwareRequestFilter;
import org.springframework.util.ReflectionUtils;
@@ -655,13 +653,15 @@ public class HttpSecurityBeanDefinitionParserTests {
public void concurrentSessionSupportAddsFilterAndExpectedBeans() throws Exception {
setContext(
"<http auto-config='true'>" +
" <concurrent-session-control session-registry-alias='seshRegistry' expired-url='/expired'/>" +
" <concurrent-session-control session-registry-alias='sr' expired-url='/expired'/>" +
"</http>" + AUTH_PROVIDER_XML);
List<Filter> filters = getFilters("/someurl");
assertTrue(filters.get(0) instanceof ConcurrentSessionFilter);
assertNotNull(appContext.getBean("seshRegistry"));
assertNotNull(getConcurrentSessionController());
assertNotNull(appContext.getBean("sr"));
SessionManagementFilter smf = (SessionManagementFilter) getFilter(SessionManagementFilter.class);
assertNotNull(smf);
checkSessionRegistry();
}
@Test
@@ -675,18 +675,18 @@ public class HttpSecurityBeanDefinitionParserTests {
checkSessionRegistry();
}
@Test(expected=BeanDefinitionParsingException.class)
public void useOfExternalConcurrentSessionControllerRequiresSessionRegistryToBeSet() throws Exception {
setContext(
"<http auto-config='true'>" +
" <concurrent-session-control session-controller-ref='sc' expired-url='/expired'/>" +
"</http>" +
"<b:bean id='sc' class='" + ConcurrentSessionControllerImpl.class.getName() +"'>" +
" <b:property name='sessionRegistry'>" +
" <b:bean class='"+ SessionRegistryImpl.class.getName() + "'/>" +
" </b:property>" +
"</b:bean>" + AUTH_PROVIDER_XML);
}
// @Test(expected=BeanDefinitionParsingException.class)
// public void useOfExternalConcurrentSessionControllerRequiresSessionRegistryToBeSet() throws Exception {
// setContext(
// "<http auto-config='true'>" +
// " <concurrent-session-control session-controller-ref='sc' expired-url='/expired'/>" +
// "</http>" +
// "<b:bean id='sc' class='" + ConcurrentSessionControllerImpl.class.getName() +"'>" +
// " <b:property name='sessionRegistry'>" +
// " <b:bean class='"+ SessionRegistryImpl.class.getName() + "'/>" +
// " </b:property>" +
// "</b:bean>" + AUTH_PROVIDER_XML);
// }
@Test
public void useOfExternalSessionControllerAndRegistryIsWiredCorrectly() throws Exception {
@@ -705,16 +705,16 @@ public class HttpSecurityBeanDefinitionParserTests {
private void checkSessionRegistry() throws Exception {
Object sessionRegistry = appContext.getBean("sr");
Object sessionRegistryFromConcurrencyFilter = FieldUtils.getFieldValue(
getFilter(ConcurrentSessionFilter.class),"sessionRegistry");
getFilter(ConcurrentSessionFilter.class), "sessionRegistry");
Object sessionRegistryFromFormLoginFilter = FieldUtils.getFieldValue(
getFilter(UsernamePasswordAuthenticationProcessingFilter.class),"sessionStrategy.sessionRegistry");
Object sessionRegistryFromController = FieldUtils.getFieldValue(getConcurrentSessionController(),"sessionRegistry");
Object sessionRegistryFromFixationFilter = FieldUtils.getFieldValue(
// Object sessionRegistryFromController = FieldUtils.getFieldValue(getConcurrentSessionController(),"sessionRegistry");
Object sessionRegistryFromMgmtFilter = FieldUtils.getFieldValue(
getFilter(SessionManagementFilter.class),"sessionStrategy.sessionRegistry");
assertSame(sessionRegistry, sessionRegistryFromConcurrencyFilter);
assertSame(sessionRegistry, sessionRegistryFromController);
assertSame(sessionRegistry, sessionRegistryFromFixationFilter);
// assertSame(sessionRegistry, sessionRegistryFromController);
assertSame(sessionRegistry, sessionRegistryFromMgmtFilter);
// SEC-1143
assertSame(sessionRegistry, sessionRegistryFromFormLoginFilter);
}
@@ -755,29 +755,25 @@ public class HttpSecurityBeanDefinitionParserTests {
"<http auto-config='true'>" +
" <concurrent-session-control max-sessions='2' exception-if-maximum-exceeded='true' />" +
"</http>" + AUTH_PROVIDER_XML);
ConcurrentSessionControllerImpl seshController = (ConcurrentSessionControllerImpl) getConcurrentSessionController();
AuthenticatedSessionStrategy seshStrategy = (AuthenticatedSessionStrategy) FieldUtils.getFieldValue(
getFilter(SessionManagementFilter.class), "sessionStrategy");
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("bob", "pass");
// Register 2 sessions and then check a third
MockHttpServletRequest req = new MockHttpServletRequest();
req.setSession(new MockHttpSession());
auth.setDetails(new WebAuthenticationDetails(req));
// req.setSession(new MockHttpSession());
// auth.setDetails(new WebAuthenticationDetails(req));
try {
seshController.checkAuthenticationAllowed(auth);
seshStrategy.onAuthentication(auth, new MockHttpServletRequest(), new MockHttpServletResponse());
} catch (ConcurrentLoginException e) {
fail("First login should be allowed");
}
seshController.registerSuccessfulAuthentication(auth);
req.setSession(new MockHttpSession());
try {
seshController.checkAuthenticationAllowed(auth);
seshStrategy.onAuthentication(auth, new MockHttpServletRequest(), new MockHttpServletResponse());
} catch (ConcurrentLoginException e) {
fail("Second login should be allowed");
}
auth.setDetails(new WebAuthenticationDetails(req));
seshController.registerSuccessfulAuthentication(auth);
req.setSession(new MockHttpSession());
auth.setDetails(new WebAuthenticationDetails(req));
seshController.checkAuthenticationAllowed(auth);
seshStrategy.onAuthentication(auth, new MockHttpServletRequest(), new MockHttpServletResponse());
}
@Test
@@ -1096,14 +1092,14 @@ public class HttpSecurityBeanDefinitionParserTests {
return ((RememberMeProcessingFilter)getFilter(RememberMeProcessingFilter.class)).getRememberMeServices();
}
@SuppressWarnings("unchecked")
private ConcurrentSessionController getConcurrentSessionController() {
Map beans = appContext.getBeansOfType(ConcurrentSessionController.class);
if (beans.size() == 0) {
return null;
}
return (ConcurrentSessionController) new ArrayList(beans.values()).get(0);
}
// @SuppressWarnings("unchecked")
// private ConcurrentSessionController getConcurrentSessionController() {
// Map beans = appContext.getBeansOfType(ConcurrentSessionController.class);
//
// if (beans.size() == 0) {
// return null;
// }
// return (ConcurrentSessionController) new ArrayList(beans.values()).get(0);
// }
}