Move "realm" attribute to be on <http> element rather than <http-basic>.

This faciltiates reuse with other mechanisms (like Digest) whilst also 
moving towards the <http-auto-configure> element (which benefits from 
having shared configuration in <http> as opposed to mechanism-specific 
elements).
This commit is contained in:
Ben Alex 2007-12-04 08:02:40 +00:00
parent d9ec944579
commit 2441ab6d9a
7 changed files with 79 additions and 51 deletions

View File

@ -1,6 +1,7 @@
package org.springframework.security.config; package org.springframework.security.config;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.BeanDefinitionParser;
@ -18,26 +19,27 @@ import org.w3c.dom.Element;
* @version $Id$ * @version $Id$
*/ */
public class BasicAuthenticationBeanDefinitionParser implements BeanDefinitionParser { public class BasicAuthenticationBeanDefinitionParser implements BeanDefinitionParser {
static final String ATT_REALM = "realm"; private String realmName;
public BasicAuthenticationBeanDefinitionParser(String realmName) {
this.realmName = realmName;
}
public BeanDefinition parse(Element elt, ParserContext parserContext) { public BeanDefinition parse(Element elt, ParserContext parserContext) {
BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder filterBuilder =
BeanDefinitionBuilder.rootBeanDefinition(BasicProcessingFilter.class); BeanDefinitionBuilder.rootBeanDefinition(BasicProcessingFilter.class);
RootBeanDefinition entryPoint = new RootBeanDefinition(BasicProcessingFilterEntryPoint.class); RootBeanDefinition entryPoint = new RootBeanDefinition(BasicProcessingFilterEntryPoint.class);
String realm = elt.getAttribute(ATT_REALM); entryPoint.getPropertyValues().addPropertyValue("realmName", realmName);
entryPoint.getPropertyValues().addPropertyValue("realmName", realm);
filterBuilder.addPropertyValue("authenticationEntryPoint", entryPoint); filterBuilder.addPropertyValue("authenticationEntryPoint", entryPoint);
parserContext.getRegistry().registerBeanDefinition(BeanIds.BASIC_AUTHENTICATION_ENTRY_POINT, entryPoint);
filterBuilder.addPropertyValue("authenticationManager", new RuntimeBeanReference(BeanIds.AUTHENTICATION_MANAGER));
filterBuilder.addPropertyValue("authenticationEntryPoint", new RuntimeBeanReference(BeanIds.BASIC_AUTHENTICATION_ENTRY_POINT));
// TODO: Remove autowiring approach from here.
// Detect auth manager
filterBuilder.setAutowireMode(RootBeanDefinition.AUTOWIRE_BY_TYPE);
parserContext.getRegistry().registerBeanDefinition(BeanIds.BASIC_AUTHENTICATION_FILTER, parserContext.getRegistry().registerBeanDefinition(BeanIds.BASIC_AUTHENTICATION_FILTER,
filterBuilder.getBeanDefinition()); filterBuilder.getBeanDefinition());
parserContext.getRegistry().registerBeanDefinition(BeanIds.BASIC_AUTHENTICATION_ENTRY_POINT, entryPoint);
return null; return null;
} }

View File

@ -44,6 +44,9 @@ import java.util.Map;
*/ */
public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
static final String ATT_REALM = "realm";
static final String DEF_REALM = "Spring Security Application";
static final String ATT_PATH_PATTERN = "pattern"; static final String ATT_PATH_PATTERN = "pattern";
static final String ATT_PATTERN_TYPE = "pathType"; static final String ATT_PATTERN_TYPE = "pathType";
static final String ATT_PATTERN_TYPE_REGEX = "regex"; static final String ATT_PATTERN_TYPE_REGEX = "regex";
@ -149,11 +152,16 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
if (formLoginElt != null) { if (formLoginElt != null) {
new FormLoginBeanDefinitionParser().parse(formLoginElt, parserContext); new FormLoginBeanDefinitionParser().parse(formLoginElt, parserContext);
} }
String realm = element.getAttribute(ATT_REALM);
if (!StringUtils.hasText(realm)) {
realm = DEF_REALM;
}
Element basicAuthElt = DomUtils.getChildElementByTagName(element, Elements.BASIC_AUTH); Element basicAuthElt = DomUtils.getChildElementByTagName(element, Elements.BASIC_AUTH);
if (basicAuthElt != null) { if (basicAuthElt != null) {
new BasicAuthenticationBeanDefinitionParser().parse(basicAuthElt, parserContext); new BasicAuthenticationBeanDefinitionParser(realm).parse(basicAuthElt, parserContext);
} }
registry.registerBeanDefinition(BeanIds.FILTER_CHAIN_PROXY, filterChainProxy); registry.registerBeanDefinition(BeanIds.FILTER_CHAIN_PROXY, filterChainProxy);

View File

@ -1,11 +1,15 @@
package org.springframework.security.config; package org.springframework.security.config;
import org.springframework.security.concurrent.ConcurrentSessionFilter; import java.util.ArrayList;
import org.springframework.security.context.HttpSessionContextIntegrationFilter; import java.util.Collections;
import org.springframework.security.ui.AbstractProcessingFilter; import java.util.Iterator;
import org.springframework.security.ui.AuthenticationEntryPoint; import java.util.List;
import org.springframework.security.ui.rememberme.RememberMeServices; import java.util.Map;
import org.springframework.security.util.FilterChainProxy;
import javax.servlet.Filter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
@ -13,23 +17,21 @@ import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.core.OrderComparator; import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.security.concurrent.ConcurrentSessionFilter;
import org.springframework.security.context.HttpSessionContextIntegrationFilter;
import org.springframework.security.ui.AbstractProcessingFilter;
import org.springframework.security.ui.AuthenticationEntryPoint;
import org.springframework.security.ui.basicauth.BasicProcessingFilter;
import org.springframework.security.ui.rememberme.RememberMeServices;
import org.springframework.security.util.FilterChainProxy;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.servlet.Filter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/** /**
* Responsible for tying up the HTTP security configuration - building ordered filter stack and linking up * Responsible for tying up the HTTP security configuration - building ordered filter stack and linking up
* with other beans. * with other beans.
* *
* @author Luke Taylor * @author Luke Taylor
* @author Ben Alex
* @version $Id$ * @version $Id$
*/ */
public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor, Ordered { public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor, Ordered {
@ -42,16 +44,16 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
ConfigUtils.configureSecurityInterceptor(beanFactory, securityInterceptor); ConfigUtils.configureSecurityInterceptor(beanFactory, securityInterceptor);
configureRememberMeSerices(beanFactory); injectUserDetailsServiceIntoRememberMeServices(beanFactory);
configureAuthenticationEntryPoint(beanFactory); injectAuthenticationEntryPointIntoExceptionTranslationFilter(beanFactory);
configureAuthenticationFilter(beanFactory); injectRememberMeServicesIntoFiltersRequiringIt(beanFactory);
configureFilterChain(beanFactory); configureFilterChain(beanFactory);
} }
private void configureRememberMeSerices(ConfigurableListableBeanFactory beanFactory) { private void injectUserDetailsServiceIntoRememberMeServices(ConfigurableListableBeanFactory beanFactory) {
try { try {
BeanDefinition rememberMeServices = BeanDefinition rememberMeServices =
beanFactory.getBeanDefinition(BeanIds.REMEMBER_ME_SERVICES); beanFactory.getBeanDefinition(BeanIds.REMEMBER_ME_SERVICES);
@ -66,7 +68,7 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
* Sets the authentication manager, (and remember-me services, if required) on any instances of * Sets the authentication manager, (and remember-me services, if required) on any instances of
* AbstractProcessingFilter * AbstractProcessingFilter
*/ */
private void configureAuthenticationFilter(ConfigurableListableBeanFactory beanFactory) { private void injectRememberMeServicesIntoFiltersRequiringIt(ConfigurableListableBeanFactory beanFactory) {
Map beans = beanFactory.getBeansOfType(RememberMeServices.class); Map beans = beanFactory.getBeansOfType(RememberMeServices.class);
RememberMeServices rememberMeServices = null; RememberMeServices rememberMeServices = null;
@ -75,29 +77,43 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
rememberMeServices = (RememberMeServices) beans.values().toArray()[0]; rememberMeServices = (RememberMeServices) beans.values().toArray()[0];
} }
Iterator authFilters = beanFactory.getBeansOfType(AbstractProcessingFilter.class).values().iterator(); // Address AbstractProcessingFilter instances
Iterator filters = beanFactory.getBeansOfType(AbstractProcessingFilter.class).values().iterator();
while (authFilters.hasNext()) { while (filters.hasNext()) {
AbstractProcessingFilter filter = (AbstractProcessingFilter) authFilters.next(); AbstractProcessingFilter filter = (AbstractProcessingFilter) filters.next();
if (rememberMeServices != null) { if (rememberMeServices != null) {
logger.info("Using RememberMeServices " + rememberMeServices + " with filter " + filter); logger.info("Using RememberMeServices " + rememberMeServices + " with filter " + filter);
filter.setRememberMeServices(rememberMeServices); filter.setRememberMeServices(rememberMeServices);
} }
} }
// Address BasicProcessingFilter instance, if it exists
// NB: For remember-me to be sent back, a user must submit a "_spring_security_remember_me" with their login request.
// Most of the time a user won't present such a parameter with their BASIC authentication request.
// In the future we might support setting the AbstractRememberMeServices.alwaysRemember = true, but I am reluctant to
// do so because it seems likely to lead to lower security for 99.99% of users if they set the property to true.
BasicProcessingFilter filter = (BasicProcessingFilter) getBeanOfType(BasicProcessingFilter.class, beanFactory);
if (filter != null && rememberMeServices != null) {
logger.info("Using RememberMeServices " + rememberMeServices + " with filter " + filter);
filter.setRememberMeServices(rememberMeServices);
}
} }
/** /**
* Selects the entry point that should be used in ExceptionTranslationFilter. Strategy is * Selects the entry point that should be used in ExceptionTranslationFilter. Strategy is
* *
* <ol> * <ol>
* <li>If only one use that.</li> * <li>If only one, use that one.</li>
* <li>If more than one, check the default interactive login Ids in order of preference</li> * <li>If more than one, use the form login entry point (if form login is being used)</li>
* <li>throw an exception (for now). TODO: Examine additional beans and types and make decision</li> * <li>If still ambiguous, throw an exception (for now). TODO: Examine additional beans and types and make decision</li>
* </ol> * </ol>
* *
*/ */
private void configureAuthenticationEntryPoint(ConfigurableListableBeanFactory beanFactory) { private void injectAuthenticationEntryPointIntoExceptionTranslationFilter(ConfigurableListableBeanFactory beanFactory) {
logger.info("Selecting AuthenticationEntryPoint for use in ExceptionTranslationFilter"); logger.info("Selecting AuthenticationEntryPoint for use in ExceptionTranslationFilter");
BeanDefinition etf = BeanDefinition etf =

View File

@ -71,6 +71,9 @@ http.attlist &=
http.attlist &= http.attlist &=
## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests. ## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.
attribute accessDecisionManager {xsd:string}? attribute accessDecisionManager {xsd:string}?
http.attlist &=
## Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to "Spring Security Application".
attribute realm {xsd:string}?
intercept-url = intercept-url =
@ -129,10 +132,9 @@ filter-chain.attlist &=
attribute filters {xsd:string} attribute filters {xsd:string}
http-basic = http-basic =
## Adds support for basic authentication ## Adds support for basic authentication (this is an element to permit future expansion, such as supporting an "ignoreFailure" attribute)
element http-basic {http-basic.attlist, empty} element http-basic {empty}
http-basic.attlist &=
attribute realm {xsd:string}
concurrent-session-control = concurrent-session-control =
## Adds support for concurrent session control, allowing limits to be placed on the number of sessions a user can have. ## Adds support for concurrent session control, allowing limits to be placed on the number of sessions a user can have.

View File

@ -146,6 +146,11 @@
<xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.</xs:documentation> <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.</xs:documentation>
</xs:annotation> </xs:annotation>
</xs:attribute> </xs:attribute>
<xs:attribute name="realm" type="xs:string">
<xs:annotation>
<xs:documentation>Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to "Spring Security Application".</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:attributeGroup> </xs:attributeGroup>
<xs:element name="intercept-url"> <xs:element name="intercept-url">
<xs:annotation> <xs:annotation>
@ -263,15 +268,10 @@
</xs:attributeGroup> </xs:attributeGroup>
<xs:element name="http-basic"> <xs:element name="http-basic">
<xs:annotation> <xs:annotation>
<xs:documentation>Adds support for basic authentication</xs:documentation> <xs:documentation>Adds support for basic authentication (this is an element to permit future expansion, such as supporting an "ignoreFailure" attribute)</xs:documentation>
</xs:annotation> </xs:annotation>
<xs:complexType> <xs:complexType/>
<xs:attributeGroup ref="security:http-basic.attlist"/>
</xs:complexType>
</xs:element> </xs:element>
<xs:attributeGroup name="http-basic.attlist">
<xs:attribute name="realm" use="required" type="xs:string"/>
</xs:attributeGroup>
<xs:element name="concurrent-session-control"> <xs:element name="concurrent-session-control">
<xs:annotation> <xs:annotation>
<xs:documentation>Adds support for concurrent session control, allowing limits to be placed on the number of sessions a user can have.</xs:documentation> <xs:documentation>Adds support for concurrent session control, allowing limits to be placed on the number of sessions a user can have.</xs:documentation>

View File

@ -15,7 +15,7 @@ http://www.springframework.org/schema/security http://www.springframework.org/sc
<form-login loginUrl="/j_spring_security_check" /> <form-login loginUrl="/j_spring_security_check" />
<!-- Default basic auth configuration. Will create filter and entry point --> <!-- Default basic auth configuration. Will create filter and entry point -->
<http-basic realm="NamespaceTestRealm" /> <http-basic/>
<!-- Default logout configuration --> <!-- Default logout configuration -->
<logout logoutUrl="/j_spring_security_logout" logoutSuccessUrl="/" invalidateSession="true" /> <logout logoutUrl="/j_spring_security_logout" logoutSuccessUrl="/" invalidateSession="true" />

View File

@ -19,7 +19,7 @@
<form-login /> <form-login />
<anonymous /> <anonymous />
<http-basic realm="SpringSecurityTutorialApp" /> <http-basic />
<logout /> <logout />
<concurrent-session-control maxSessions="1" exceptionIfMaximumExceeded="true"/> <concurrent-session-control maxSessions="1" exceptionIfMaximumExceeded="true"/>