mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-03-09 06:50:05 +00:00
SEC-821: Added support for eternal session registry and concurrent session controller to the 2.0.2 namespace.
This commit is contained in:
parent
8b5bbe3800
commit
d63536cc0d
@ -3,27 +3,40 @@ package org.springframework.security.config;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Just registers an alias name for the default ProviderManager used by the namespace
|
||||
* Registers an alias name for the default ProviderManager used by the namespace
|
||||
* configuration, allowing users to reference it in their beans and clearly see where the name is
|
||||
* coming from.
|
||||
* coming from. Also allows the ConcurrentSessionController to be set on the ProviderManager.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class AuthenticationManagerBeanDefinitionParser implements BeanDefinitionParser {
|
||||
private static final String ATT_ALIAS = "alias";
|
||||
private static final String ATT_SESSION_CONTROLLER_REF = "session-controller-ref";
|
||||
private static final String ATT_ALIAS = "alias";
|
||||
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
BeanDefinition authManager = ConfigUtils.registerProviderManagerIfNecessary(parserContext);
|
||||
|
||||
String alias = element.getAttribute(ATT_ALIAS);
|
||||
|
||||
if (!StringUtils.hasText(alias)) {
|
||||
parserContext.getReaderContext().error(ATT_ALIAS + " is required.", element );
|
||||
}
|
||||
|
||||
String sessionControllerRef = element.getAttribute(ATT_SESSION_CONTROLLER_REF);
|
||||
|
||||
if (StringUtils.hasText(sessionControllerRef)) {
|
||||
ConfigUtils.setSessionControllerOnAuthenticationManager(parserContext,
|
||||
BeanIds.CONCURRENT_SESSION_CONTROLLER, element);
|
||||
authManager.getPropertyValues().addPropertyValue("sessionController",
|
||||
new RuntimeBeanReference(sessionControllerRef));
|
||||
}
|
||||
|
||||
parserContext.getRegistry().registerAlias(BeanIds.AUTHENTICATION_MANAGER, alias);
|
||||
|
||||
|
@ -29,7 +29,8 @@ public class ConcurrentSessionsBeanDefinitionParser implements BeanDefinitionPar
|
||||
static final String ATT_EXPIRY_URL = "expired-url";
|
||||
static final String ATT_MAX_SESSIONS = "max-sessions";
|
||||
static final String ATT_EXCEPTION_IF_MAX_EXCEEDED = "exception-if-maximum-exceeded";
|
||||
static final String ATT_SESSION_REGISTRY_ALIAS = "session-registry-alias";
|
||||
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 parserContext) {
|
||||
CompositeComponentDefinition compositeDef =
|
||||
@ -38,26 +39,43 @@ public class ConcurrentSessionsBeanDefinitionParser implements BeanDefinitionPar
|
||||
|
||||
BeanDefinitionRegistry beanRegistry = parserContext.getRegistry();
|
||||
|
||||
RootBeanDefinition sessionRegistry = new RootBeanDefinition(SessionRegistryImpl.class);
|
||||
String sessionRegistryId = element.getAttribute(ATT_SESSION_REGISTRY_REF);
|
||||
|
||||
if (!StringUtils.hasText(sessionRegistryId)) {
|
||||
RootBeanDefinition sessionRegistry = new RootBeanDefinition(SessionRegistryImpl.class);
|
||||
beanRegistry.registerBeanDefinition(BeanIds.SESSION_REGISTRY, sessionRegistry);
|
||||
parserContext.registerComponent(new BeanComponentDefinition(sessionRegistry, BeanIds.SESSION_REGISTRY));
|
||||
sessionRegistryId = BeanIds.SESSION_REGISTRY;
|
||||
} else {
|
||||
// Register the default ID as an alias so that things like session fixation filter can access it
|
||||
beanRegistry.registerAlias(sessionRegistryId, BeanIds.SESSION_REGISTRY);
|
||||
}
|
||||
|
||||
String registryAlias = element.getAttribute(ATT_SESSION_REGISTRY_ALIAS);
|
||||
if (StringUtils.hasText(registryAlias)) {
|
||||
beanRegistry.registerAlias(sessionRegistryId, registryAlias);
|
||||
}
|
||||
|
||||
BeanDefinitionBuilder filterBuilder =
|
||||
BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionFilter.class);
|
||||
BeanDefinitionBuilder controllerBuilder
|
||||
= BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionControllerImpl.class);
|
||||
controllerBuilder.addPropertyValue("sessionRegistry", new RuntimeBeanReference(BeanIds.SESSION_REGISTRY));
|
||||
filterBuilder.addPropertyValue("sessionRegistry", new RuntimeBeanReference(BeanIds.SESSION_REGISTRY));
|
||||
filterBuilder.addPropertyValue("sessionRegistry", new RuntimeBeanReference(sessionRegistryId));
|
||||
|
||||
Object source = parserContext.extractSource(element);
|
||||
filterBuilder.setSource(source);
|
||||
filterBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
controllerBuilder.setSource(source);
|
||||
controllerBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
|
||||
|
||||
String expiryUrl = element.getAttribute(ATT_EXPIRY_URL);
|
||||
|
||||
if (StringUtils.hasText(expiryUrl)) {
|
||||
ConfigUtils.validateHttpRedirect(expiryUrl, parserContext, source);
|
||||
filterBuilder.addPropertyValue("expiredUrl", expiryUrl);
|
||||
}
|
||||
}
|
||||
|
||||
BeanDefinitionBuilder controllerBuilder
|
||||
= BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionControllerImpl.class);
|
||||
controllerBuilder.setSource(source);
|
||||
controllerBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
controllerBuilder.addPropertyValue("sessionRegistry", new RuntimeBeanReference(sessionRegistryId));
|
||||
|
||||
String maxSessions = element.getAttribute(ATT_MAX_SESSIONS);
|
||||
|
||||
@ -72,23 +90,14 @@ public class ConcurrentSessionsBeanDefinitionParser implements BeanDefinitionPar
|
||||
}
|
||||
|
||||
BeanDefinition controller = controllerBuilder.getBeanDefinition();
|
||||
beanRegistry.registerBeanDefinition(BeanIds.SESSION_REGISTRY, sessionRegistry);
|
||||
parserContext.registerComponent(new BeanComponentDefinition(sessionRegistry, BeanIds.SESSION_REGISTRY));
|
||||
|
||||
String registryAlias = element.getAttribute(ATT_SESSION_REGISTRY_ALIAS);
|
||||
if (StringUtils.hasText(registryAlias)) {
|
||||
beanRegistry.registerAlias(BeanIds.SESSION_REGISTRY, registryAlias);
|
||||
}
|
||||
|
||||
beanRegistry.registerBeanDefinition(BeanIds.CONCURRENT_SESSION_CONTROLLER, controller);
|
||||
parserContext.registerComponent(new BeanComponentDefinition(controller, BeanIds.CONCURRENT_SESSION_CONTROLLER));
|
||||
beanRegistry.registerBeanDefinition(BeanIds.CONCURRENT_SESSION_FILTER, filterBuilder.getBeanDefinition());
|
||||
parserContext.registerComponent(new BeanComponentDefinition(filterBuilder.getBeanDefinition(), BeanIds.CONCURRENT_SESSION_FILTER));
|
||||
ConfigUtils.addHttpFilter(parserContext, new RuntimeBeanReference(BeanIds.CONCURRENT_SESSION_FILTER));
|
||||
|
||||
BeanDefinition providerManager = ConfigUtils.registerProviderManagerIfNecessary(parserContext);
|
||||
|
||||
providerManager.getPropertyValues().addPropertyValue("sessionController", controller);
|
||||
ConfigUtils.setSessionControllerOnAuthenticationManager(parserContext, BeanIds.CONCURRENT_SESSION_CONTROLLER, element);
|
||||
|
||||
parserContext.popAndRegisterContainingComponent();
|
||||
|
||||
|
@ -7,6 +7,7 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.PropertyValue;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
@ -22,6 +23,7 @@ import org.springframework.security.vote.AffirmativeBased;
|
||||
import org.springframework.security.vote.AuthenticatedVoter;
|
||||
import org.springframework.security.vote.RoleVoter;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Utility methods used internally by the Spring Security namespace configuration code.
|
||||
@ -168,4 +170,17 @@ public abstract class ConfigUtils {
|
||||
}
|
||||
pc.getReaderContext().warning(url + " is not a valid redirect URL (must start with '/' or http(s))", source);
|
||||
}
|
||||
|
||||
static void setSessionControllerOnAuthenticationManager(ParserContext pc, String beanName, Element sourceElt) {
|
||||
BeanDefinition authManager = registerProviderManagerIfNecessary(pc);
|
||||
PropertyValue pv = authManager.getPropertyValues().getPropertyValue("sessionController");
|
||||
|
||||
if (pv != null && pv.getValue() != null) {
|
||||
pc.getReaderContext().error("A session controller has already been set on the authentication manager. " +
|
||||
"The <concurrent-session-control> element isn't compatible with a custom session controller",
|
||||
pc.extractSource(sourceElt));
|
||||
}
|
||||
|
||||
authManager.getPropertyValues().addPropertyValue("sessionController", new RuntimeBeanReference(beanName));
|
||||
}
|
||||
}
|
||||
|
@ -335,13 +335,17 @@ concurrent-session-control =
|
||||
concurrent-sessions.attlist &=
|
||||
attribute max-sessions {xsd:positiveInteger}?
|
||||
concurrent-sessions.attlist &=
|
||||
## The URL a user will be redirected to if they attempt to use a session which has been "expired" by the concurrent session controller.
|
||||
attribute expired-url {xsd:string}?
|
||||
concurrent-sessions.attlist &=
|
||||
## Specifies that an exception should be raised when a user attempts to login twice. The default behaviour is to expire the original session.
|
||||
attribute exception-if-maximum-exceeded {boolean}?
|
||||
concurrent-sessions.attlist &=
|
||||
## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration
|
||||
attribute session-registry-alias {xsd:string}?
|
||||
|
||||
concurrent-sessions.attlist &=
|
||||
## A reference to an external SessionRegistry implementation which will be used in place of the standard one.
|
||||
attribute session-registry-ref {xsd:string}?
|
||||
|
||||
remember-me =
|
||||
## Sets up remember-me authentication. If used with the "key" attribute (or no attributes) the cookie-only implementation will be used. Specifying "token-repository-ref" or "remember-me-data-source-ref" will use the more secure, persisten token approach.
|
||||
@ -408,11 +412,15 @@ x509.attlist &=
|
||||
user-service-ref?
|
||||
|
||||
authentication-manager =
|
||||
## If you are using namespace configuration with Spring Security, an AuthenticationManager will automatically be registered. This element simple allows you to define an alias to allow you to reference the authentication-manager in your own beans.
|
||||
## If you are using namespace configuration with Spring Security, an AuthenticationManager will automatically be registered. This element allows you to define an alias to allow you to reference the authentication-manager in your own beans.
|
||||
element authentication-manager {authman.attlist}
|
||||
## The alias you wish to use for the AuthenticationManager bean
|
||||
authman.attlist &=
|
||||
## The alias you wish to use for the AuthenticationManager bean
|
||||
attribute alias {xsd:ID}
|
||||
authman.attlist &=
|
||||
## Allows the session controller to be set on the internal AuthenticationManager. This should not be used with the <concurrent-session-control /> element
|
||||
attribute session-controller-ref {xsd:string}?
|
||||
|
||||
|
||||
authentication-provider =
|
||||
## Indicates that the contained user-service should be used as an authentication source.
|
||||
|
@ -489,7 +489,16 @@
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element maxOccurs="unbounded" ref="security:protect"/>
|
||||
<xs:element maxOccurs="unbounded" name="protect">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Defines a protected method and the access control configuration
|
||||
attributes that apply to it. We strongly advise you NOT to mix "protect" declarations
|
||||
with any services provided "global-method-security".</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:attributeGroup ref="security:protect.attlist"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
<xs:attributeGroup ref="security:intercept-methods.attlist"/>
|
||||
</xs:complexType>
|
||||
@ -502,16 +511,6 @@
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
<xs:element name="protect">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Defines a protected method and the access control configuration attributes
|
||||
that apply to it. We strongly advise you NOT to mix "protect" declarations with any services
|
||||
provided "global-method-security".</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:attributeGroup ref="security:protect.attlist"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:attributeGroup name="protect.attlist">
|
||||
<xs:attribute name="method" use="required" type="xs:string">
|
||||
<xs:annotation>
|
||||
@ -1014,14 +1013,30 @@
|
||||
</xs:attributeGroup>
|
||||
<xs:attributeGroup name="concurrent-sessions.attlist">
|
||||
<xs:attribute name="max-sessions" type="xs:positiveInteger"/>
|
||||
<xs:attribute name="expired-url" type="xs:string"/>
|
||||
<xs:attribute name="exception-if-maximum-exceeded" type="security:boolean"/>
|
||||
<xs:attribute name="expired-url" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The URL a user will be redirected to if they attempt to use a session
|
||||
which has been "expired" by the concurrent session controller.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="exception-if-maximum-exceeded" type="security:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies that an exception should be raised when a user attempts to login
|
||||
twice. The default behaviour is to expire the original session.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="session-registry-alias" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to
|
||||
access it in your own configuration</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="session-registry-ref" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A reference to an external SessionRegistry implementation which will be
|
||||
used in place of the standard one. </xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
<xs:attributeGroup name="remember-me.attlist">
|
||||
<xs:attribute name="key" type="xs:string">
|
||||
@ -1130,8 +1145,8 @@
|
||||
<xs:element name="authentication-manager">
|
||||
<xs:annotation>
|
||||
<xs:documentation>If you are using namespace configuration with Spring Security, an
|
||||
AuthenticationManager will automatically be registered. This element simple allows you to
|
||||
define an alias to allow you to reference the authentication-manager in your own beans.
|
||||
AuthenticationManager will automatically be registered. This element allows you to define an
|
||||
alias to allow you to reference the authentication-manager in your own beans.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
@ -1139,11 +1154,19 @@
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:attributeGroup name="authman.attlist">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The alias you wish to use for the AuthenticationManager
|
||||
bean</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attribute name="alias" use="required" type="xs:ID"/>
|
||||
<xs:attribute name="alias" use="required" type="xs:ID">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The alias you wish to use for the AuthenticationManager
|
||||
bean</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="session-controller-ref" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Allows the session controller to be set on the internal
|
||||
AuthenticationManager. This should not be used with the <concurrent-session-control
|
||||
/> element</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
<xs:element name="authentication-provider">
|
||||
<xs:annotation>
|
||||
|
@ -8,7 +8,6 @@ import java.util.List;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
@ -40,7 +39,6 @@ import org.springframework.security.ui.rememberme.NullRememberMeServices;
|
||||
import org.springframework.security.ui.rememberme.PersistentTokenBasedRememberMeServices;
|
||||
import org.springframework.security.ui.rememberme.RememberMeProcessingFilter;
|
||||
import org.springframework.security.ui.rememberme.RememberMeServices;
|
||||
import org.springframework.security.ui.rememberme.TokenBasedRememberMeServices;
|
||||
import org.springframework.security.ui.webapp.AuthenticationProcessingFilter;
|
||||
import org.springframework.security.ui.webapp.DefaultLoginPageGeneratingFilter;
|
||||
import org.springframework.security.util.FieldUtils;
|
||||
@ -405,7 +403,7 @@ public class HttpSecurityBeanDefinitionParserTests {
|
||||
setContext(
|
||||
"<http auto-config='true'>" +
|
||||
" <concurrent-session-control session-registry-alias='seshRegistry' expired-url='/expired'/>" +
|
||||
"</http>" + AUTH_PROVIDER_XML);
|
||||
"</http>" + AUTH_PROVIDER_XML);
|
||||
List filters = getFilters("/someurl");
|
||||
|
||||
assertTrue(filters.get(0) instanceof ConcurrentSessionFilter);
|
||||
@ -413,6 +411,55 @@ public class HttpSecurityBeanDefinitionParserTests {
|
||||
assertNotNull(appContext.getBean(BeanIds.CONCURRENT_SESSION_CONTROLLER));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void externalSessionRegistryBeanIsConfiguredCorrectly() throws Exception {
|
||||
setContext(
|
||||
"<http auto-config='true'>" +
|
||||
" <concurrent-session-control session-registry-ref='seshRegistry' />" +
|
||||
"</http>" +
|
||||
"<b:bean id='seshRegistry' class='org.springframework.security.concurrent.SessionRegistryImpl'/>" +
|
||||
AUTH_PROVIDER_XML);
|
||||
Object sessionRegistry = appContext.getBean("seshRegistry");
|
||||
Object sessionRegistryFromFilter = FieldUtils.getFieldValue(
|
||||
appContext.getBean(BeanIds.CONCURRENT_SESSION_FILTER),"sessionRegistry");
|
||||
Object sessionRegistryFromController = FieldUtils.getFieldValue(
|
||||
appContext.getBean(BeanIds.CONCURRENT_SESSION_CONTROLLER),"sessionRegistry");
|
||||
Object sessionRegistryFromFixationFilter = FieldUtils.getFieldValue(
|
||||
appContext.getBean(BeanIds.SESSION_FIXATION_PROTECTION_FILTER),"sessionRegistry");
|
||||
|
||||
assertSame(sessionRegistry, sessionRegistryFromFilter);
|
||||
assertSame(sessionRegistry, sessionRegistryFromController);
|
||||
assertSame(sessionRegistry, sessionRegistryFromFixationFilter);
|
||||
}
|
||||
|
||||
@Test(expected=BeanDefinitionParsingException.class)
|
||||
public void concurrentSessionSupportCantBeUsedWithIndependentControllerBean() throws Exception {
|
||||
setContext(
|
||||
"<authentication-manager alias='authManager' session-controller-ref='sc'/>" +
|
||||
"<http auto-config='true'>" +
|
||||
" <concurrent-session-control session-registry-alias='seshRegistry' expired-url='/expired'/>" +
|
||||
"</http>" +
|
||||
"<b:bean id='sc' class='org.springframework.security.concurrent.ConcurrentSessionControllerImpl'>" +
|
||||
" <b:property name='sessionRegistry'>" +
|
||||
" <b:bean class='org.springframework.security.concurrent.SessionRegistryImpl'/>" +
|
||||
" </b:property>" +
|
||||
"</b:bean>" + AUTH_PROVIDER_XML);
|
||||
}
|
||||
|
||||
@Test(expected=BeanDefinitionParsingException.class)
|
||||
public void concurrentSessionSupportCantBeUsedWithIndependentControllerBean2() throws Exception {
|
||||
setContext(
|
||||
"<http auto-config='true'>" +
|
||||
" <concurrent-session-control session-registry-alias='seshRegistry' expired-url='/expired'/>" +
|
||||
"</http>" +
|
||||
"<b:bean id='sc' class='org.springframework.security.concurrent.ConcurrentSessionControllerImpl'>" +
|
||||
" <b:property name='sessionRegistry'>" +
|
||||
" <b:bean class='org.springframework.security.concurrent.SessionRegistryImpl'/>" +
|
||||
" </b:property>" +
|
||||
"</b:bean>" +
|
||||
"<authentication-manager alias='authManager' session-controller-ref='sc'/>" + AUTH_PROVIDER_XML);
|
||||
}
|
||||
|
||||
@Test(expected=ConcurrentLoginException.class)
|
||||
public void concurrentSessionMaxSessionsIsCorrectlyConfigured() throws Exception {
|
||||
setContext(
|
||||
|
Loading…
x
Reference in New Issue
Block a user