SEC-1657: Added support for 'name' attribute in <http> element to expose filter chain as a list bean.

This commit is contained in:
Luke Taylor 2011-01-14 17:21:22 +00:00
parent f20649f035
commit 2eefbf3a23
5 changed files with 61 additions and 6 deletions

View File

@ -2,6 +2,7 @@ package org.springframework.security.config.http;
import org.springframework.beans.BeanMetadataElement; import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanReference;
import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.ManagedMap; import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.BeanDefinitionParser;
@ -26,8 +27,9 @@ public class HttpFirewallBeanDefinitionParser implements BeanDefinitionParser {
pc.getReaderContext().error("ref attribute is required", pc.extractSource(element)); pc.getReaderContext().error("ref attribute is required", pc.extractSource(element));
} }
// Ensure the FCP is registered.
HttpSecurityBeanDefinitionParser.registerFilterChainProxy(pc, HttpSecurityBeanDefinitionParser.registerFilterChainProxy(pc,
new ManagedMap<BeanDefinition, List<BeanMetadataElement>>(), new ManagedMap<BeanDefinition, BeanReference>(),
pc.extractSource(element)); pc.extractSource(element));
BeanDefinition filterChainProxy = pc.getRegistry().getBeanDefinition(BeanIds.FILTER_CHAIN_PROXY); BeanDefinition filterChainProxy = pc.getRegistry().getBeanDefinition(BeanIds.FILTER_CHAIN_PROXY);
filterChainProxy.getPropertyValues().addPropertyValue("firewall", new RuntimeBeanReference(ref)); filterChainProxy.getPropertyValues().addPropertyValue("firewall", new RuntimeBeanReference(ref));

View File

@ -10,6 +10,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanMetadataElement; import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanReference; import org.springframework.beans.factory.config.BeanReference;
import org.springframework.beans.factory.config.ListFactoryBean;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean; import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder; import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
@ -72,7 +73,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
pc.pushContainingComponent(compositeDef); pc.pushContainingComponent(compositeDef);
MatcherType matcherType = MatcherType.fromElement(element); MatcherType matcherType = MatcherType.fromElement(element);
ManagedMap<BeanDefinition, List<BeanMetadataElement>> filterChainMap = new ManagedMap<BeanDefinition, List<BeanMetadataElement>>(); ManagedMap<BeanDefinition, BeanReference> filterChainMap = new ManagedMap<BeanDefinition, BeanReference>();
String filterChainPattern = element.getAttribute(ATT_PATH_PATTERN); String filterChainPattern = element.getAttribute(ATT_PATH_PATTERN);
@ -92,7 +93,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
return null; return null;
} }
List<BeanMetadataElement> createFilterChain(Element element, ParserContext pc, MatcherType matcherType) { BeanReference createFilterChain(Element element, ParserContext pc, MatcherType matcherType) {
boolean secured = !OPT_SECURITY_NONE.equals(element.getAttribute(ATT_SECURED)); boolean secured = !OPT_SECURITY_NONE.equals(element.getAttribute(ATT_SECURED));
if (!secured) { if (!secured) {
@ -108,7 +109,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
} }
} }
return Collections.emptyList(); return createFilterListBean(element, pc, Collections.emptyList());
} }
final String portMapperName = createPortMapper(element, pc); final String portMapperName = createPortMapper(element, pc);
@ -140,9 +141,24 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
filterChain.add(od.bean); filterChain.add(od.bean);
} }
return filterChain; return createFilterListBean(element, pc, filterChain);
} }
private BeanReference createFilterListBean(Element element, ParserContext pc, List<?> filterChain) {
BeanDefinition listFactoryBean = new RootBeanDefinition(ListFactoryBean.class);
String id = element.getAttribute("name");
if (!StringUtils.hasText(id)) {
id = element.getAttribute("id");
if (!StringUtils.hasText(id)) {
id = pc.getReaderContext().generateBeanName(listFactoryBean);
}
}
listFactoryBean.getPropertyValues().add("sourceList", filterChain);
pc.registerBeanComponent(new BeanComponentDefinition(listFactoryBean, id));
return new RuntimeBeanReference(id);
}
private String createPortMapper(Element elt, ParserContext pc) { private String createPortMapper(Element elt, ParserContext pc) {
// Register the portMapper. A default will always be created, even if no element exists. // Register the portMapper. A default will always be created, even if no element exists.
@ -247,7 +263,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
static void registerFilterChainProxy(ParserContext pc, Map<BeanDefinition, List<BeanMetadataElement>> filterChainMap, Object source) { static void registerFilterChainProxy(ParserContext pc, Map<BeanDefinition, BeanReference> filterChainMap, Object source) {
if (pc.getRegistry().containsBeanDefinition(BeanIds.FILTER_CHAIN_PROXY)) { if (pc.getRegistry().containsBeanDefinition(BeanIds.FILTER_CHAIN_PROXY)) {
// Already registered. Obtain the filter chain map and add the new entries to it // Already registered. Obtain the filter chain map and add the new entries to it

View File

@ -25,6 +25,9 @@ url =
id = id =
## A bean identifier, used for referring to the bean elsewhere in the context. ## A bean identifier, used for referring to the bean elsewhere in the context.
attribute id {xsd:ID} attribute id {xsd:ID}
name =
## A bean identifier, used for referring to the bean elsewhere in the context.
attribute name {xsd:ID}
ref = ref =
## Defines a reference to a Spring bean Id. ## Defines a reference to a Spring bean Id.
attribute ref {xsd:token} attribute ref {xsd:token}
@ -311,6 +314,10 @@ http.attlist &=
http.attlist &= http.attlist &=
## Prevents the jsessionid parameter from being added to rendered URLs. ## Prevents the jsessionid parameter from being added to rendered URLs.
attribute disable-url-rewriting {xsd:boolean}? attribute disable-url-rewriting {xsd:boolean}?
http.attlist &=
## Exposes the list of filters defined by this configuration under this bean name in the application context. May be used by
name?
access-denied-handler = access-denied-handler =
## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance. ## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance.

View File

@ -70,6 +70,13 @@
</xs:annotation> </xs:annotation>
</xs:attribute> </xs:attribute>
</xs:attributeGroup> </xs:attributeGroup>
<xs:attributeGroup name="name">
<xs:attribute name="name" use="required" type="xs:ID">
<xs:annotation>
<xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:attributeGroup name="ref"> <xs:attributeGroup name="ref">
<xs:attribute name="ref" use="required" type="xs:token"> <xs:attribute name="ref" use="required" type="xs:token">
<xs:annotation> <xs:annotation>
@ -781,6 +788,11 @@
<xs:documentation>Prevents the jsessionid parameter from being added to rendered URLs.</xs:documentation> <xs:documentation>Prevents the jsessionid parameter from being added to rendered URLs.</xs:documentation>
</xs:annotation> </xs:annotation>
</xs:attribute> </xs:attribute>
<xs:attribute name="name" type="xs:ID">
<xs:annotation>
<xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:attributeGroup> </xs:attributeGroup>
<xs:attributeGroup name="access-denied-handler.attlist"> <xs:attributeGroup name="access-denied-handler.attlist">

View File

@ -3,6 +3,7 @@ package org.springframework.security.config.http
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException import org.springframework.beans.factory.parsing.BeanDefinitionParsingException
import org.springframework.security.config.BeanIds import org.springframework.security.config.BeanIds
import org.springframework.security.web.FilterChainProxy import org.springframework.security.web.FilterChainProxy
import org.junit.Assert
/** /**
* Tests scenarios with multiple &lt;http&gt; elements. * Tests scenarios with multiple &lt;http&gt; elements.
@ -40,4 +41,21 @@ class MultiHttpBlockConfigTests extends AbstractHttpConfigTests {
then: then:
thrown(BeanDefinitionParsingException) thrown(BeanDefinitionParsingException)
} }
def namedFilterChainIsExposedAsABean () {
xml.http(name: 'basic', pattern: '/basic/**', 'create-session': 'stateless') {
'http-basic'()
}
xml.http(pattern: '/form/**') {
'form-login'()
}
createAppContext()
def fcp = appContext.getBean(BeanIds.FILTER_CHAIN_PROXY)
List filterChains = fcp.getFilterChainMap().values() as List;
List basicChain = filterChains[0];
expect:
Assert.assertSame (basicChain, appContext.getBean('basic'))
}
} }