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;
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.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
@ -18,26 +19,27 @@ import org.w3c.dom.Element;
* @version $Id$
*/
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) {
BeanDefinitionBuilder filterBuilder =
BeanDefinitionBuilder.rootBeanDefinition(BasicProcessingFilter.class);
RootBeanDefinition entryPoint = new RootBeanDefinition(BasicProcessingFilterEntryPoint.class);
String realm = elt.getAttribute(ATT_REALM);
entryPoint.getPropertyValues().addPropertyValue("realmName", realm);
entryPoint.getPropertyValues().addPropertyValue("realmName", realmName);
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,
filterBuilder.getBeanDefinition());
parserContext.getRegistry().registerBeanDefinition(BeanIds.BASIC_AUTHENTICATION_ENTRY_POINT, entryPoint);
return null;
}

View File

@ -44,6 +44,9 @@ import java.util.Map;
*/
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_PATTERN_TYPE = "pathType";
static final String ATT_PATTERN_TYPE_REGEX = "regex";
@ -149,11 +152,16 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
if (formLoginElt != null) {
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);
if (basicAuthElt != null) {
new BasicAuthenticationBeanDefinitionParser().parse(basicAuthElt, parserContext);
new BasicAuthenticationBeanDefinitionParser(realm).parse(basicAuthElt, parserContext);
}
registry.registerBeanDefinition(BeanIds.FILTER_CHAIN_PROXY, filterChainProxy);

View File

@ -1,11 +1,15 @@
package org.springframework.security.config;
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.rememberme.RememberMeServices;
import org.springframework.security.util.FilterChainProxy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
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.factory.NoSuchBeanDefinitionException;
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.core.OrderComparator;
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.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
* with other beans.
*
* @author Luke Taylor
* @author Ben Alex
* @version $Id$
*/
public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor, Ordered {
@ -42,16 +44,16 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
ConfigUtils.configureSecurityInterceptor(beanFactory, securityInterceptor);
configureRememberMeSerices(beanFactory);
injectUserDetailsServiceIntoRememberMeServices(beanFactory);
configureAuthenticationEntryPoint(beanFactory);
injectAuthenticationEntryPointIntoExceptionTranslationFilter(beanFactory);
configureAuthenticationFilter(beanFactory);
injectRememberMeServicesIntoFiltersRequiringIt(beanFactory);
configureFilterChain(beanFactory);
}
private void configureRememberMeSerices(ConfigurableListableBeanFactory beanFactory) {
private void injectUserDetailsServiceIntoRememberMeServices(ConfigurableListableBeanFactory beanFactory) {
try {
BeanDefinition rememberMeServices =
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
* AbstractProcessingFilter
*/
private void configureAuthenticationFilter(ConfigurableListableBeanFactory beanFactory) {
private void injectRememberMeServicesIntoFiltersRequiringIt(ConfigurableListableBeanFactory beanFactory) {
Map beans = beanFactory.getBeansOfType(RememberMeServices.class);
RememberMeServices rememberMeServices = null;
@ -75,29 +77,43 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
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()) {
AbstractProcessingFilter filter = (AbstractProcessingFilter) authFilters.next();
while (filters.hasNext()) {
AbstractProcessingFilter filter = (AbstractProcessingFilter) filters.next();
if (rememberMeServices != null) {
logger.info("Using RememberMeServices " + rememberMeServices + " with filter " + filter);
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
*
* <ol>
* <li>If only one use that.</li>
* <li>If more than one, check the default interactive login Ids in order of preference</li>
* <li>throw an exception (for now). TODO: Examine additional beans and types and make decision</li>
* <li>If only one, use that one.</li>
* <li>If more than one, use the form login entry point (if form login is being used)</li>
* <li>If still ambiguous, throw an exception (for now). TODO: Examine additional beans and types and make decision</li>
* </ol>
*
*/
private void configureAuthenticationEntryPoint(ConfigurableListableBeanFactory beanFactory) {
private void injectAuthenticationEntryPointIntoExceptionTranslationFilter(ConfigurableListableBeanFactory beanFactory) {
logger.info("Selecting AuthenticationEntryPoint for use in ExceptionTranslationFilter");
BeanDefinition etf =

View File

@ -71,6 +71,9 @@ http.attlist &=
http.attlist &=
## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.
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 =
@ -129,10 +132,9 @@ filter-chain.attlist &=
attribute filters {xsd:string}
http-basic =
## Adds support for basic authentication
element http-basic {http-basic.attlist, empty}
http-basic.attlist &=
attribute realm {xsd:string}
## Adds support for basic authentication (this is an element to permit future expansion, such as supporting an "ignoreFailure" attribute)
element http-basic {empty}
concurrent-session-control =
## 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:annotation>
</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:element name="intercept-url">
<xs:annotation>
@ -263,15 +268,10 @@
</xs:attributeGroup>
<xs:element name="http-basic">
<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:complexType>
<xs:attributeGroup ref="security:http-basic.attlist"/>
</xs:complexType>
<xs:complexType/>
</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: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>

View File

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

View File

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