Przeglądaj źródła

SEC-1584: Added namespace support for injecting custom HttpFirewall instance into FilterChainProxy.

Luke Taylor 15 lat temu
rodzic
commit
173537f4f2

+ 1 - 0
config/src/main/java/org/springframework/security/config/Elements.java

@@ -53,4 +53,5 @@ public abstract class Elements {
     public static final String FILTER_INVOCATION_DEFINITION_SOURCE = "filter-invocation-definition-source";
     public static final String LDAP_PASSWORD_COMPARE = "password-compare";
     public static final String DEBUG = "debug";
+    public static final String HTTP_FIREWALL = "http-firewall";
 }

+ 3 - 1
config/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java

@@ -15,6 +15,7 @@ import org.springframework.security.config.authentication.JdbcUserServiceBeanDef
 import org.springframework.security.config.authentication.UserServiceBeanDefinitionParser;
 import org.springframework.security.config.http.FilterChainMapBeanDefinitionDecorator;
 import org.springframework.security.config.http.FilterInvocationSecurityMetadataSourceParser;
+import org.springframework.security.config.http.HttpFirewallBeanDefinitionParser;
 import org.springframework.security.config.http.HttpSecurityBeanDefinitionParser;
 import org.springframework.security.config.ldap.LdapProviderBeanDefinitionParser;
 import org.springframework.security.config.ldap.LdapServerBeanDefinitionParser;
@@ -116,11 +117,12 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
         parsers.put(Elements.GLOBAL_METHOD_SECURITY, new GlobalMethodSecurityBeanDefinitionParser());
         parsers.put(Elements.AUTHENTICATION_MANAGER, new AuthenticationManagerBeanDefinitionParser());
         parsers.put(Elements.METHOD_SECURITY_METADATA_SOURCE, new MethodSecurityMetadataSourceBeanDefinitionParser());
-        parsers.put(Elements.DEBUG, new DebugBeanDefinitionParser());
 
         // Only load the web-namespace parsers if the web classes are available
         if (ClassUtils.isPresent("org.springframework.security.web.FilterChainProxy", getClass().getClassLoader())) {
+            parsers.put(Elements.DEBUG, new DebugBeanDefinitionParser());
             parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser());
+            parsers.put(Elements.HTTP_FIREWALL, new HttpFirewallBeanDefinitionParser());
             parsers.put(Elements.FILTER_INVOCATION_DEFINITION_SOURCE, new FilterInvocationSecurityMetadataSourceParser());
             parsers.put(Elements.FILTER_SECURITY_METADATA_SOURCE, new FilterInvocationSecurityMetadataSourceParser());
             filterChainMapBDD = new FilterChainMapBeanDefinitionDecorator();

+ 38 - 0
config/src/main/java/org/springframework/security/config/http/HttpFirewallBeanDefinitionParser.java

@@ -0,0 +1,38 @@
+package org.springframework.security.config.http;
+
+import org.springframework.beans.BeanMetadataElement;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
+import org.springframework.beans.factory.support.ManagedMap;
+import org.springframework.beans.factory.xml.BeanDefinitionParser;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.security.config.BeanIds;
+import org.springframework.util.StringUtils;
+import org.w3c.dom.Element;
+
+import java.util.*;
+
+/**
+ * Injects the supplied {@code HttpFirewall} bean reference into the {@code FilterChainProxy}.
+ *
+ * @author Luke Taylor
+ */
+public class HttpFirewallBeanDefinitionParser implements BeanDefinitionParser {
+
+    @Override
+    public BeanDefinition parse(Element element, ParserContext pc) {
+        String ref = element.getAttribute("ref");
+
+        if (!StringUtils.hasText(ref)) {
+            pc.getReaderContext().error("ref attribute is required", pc.extractSource(element));
+        }
+
+        HttpSecurityBeanDefinitionParser.registerFilterChainProxy(pc,
+                new ManagedMap<BeanDefinition, List<BeanMetadataElement>>(),
+                pc.extractSource(element));
+        BeanDefinition filterChainProxy = pc.getRegistry().getBeanDefinition(BeanIds.FILTER_CHAIN_PROXY);
+        filterChainProxy.getPropertyValues().addPropertyValue("firewall", new RuntimeBeanReference(ref));
+
+        return null;
+    }
+}

+ 1 - 2
config/src/main/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParser.java

@@ -247,10 +247,9 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
     }
 
     @SuppressWarnings("unchecked")
-    private void registerFilterChainProxy(ParserContext pc, Map<BeanDefinition, List<BeanMetadataElement>> filterChainMap, Object source) {
+    static void registerFilterChainProxy(ParserContext pc, Map<BeanDefinition, List<BeanMetadataElement>> filterChainMap, Object source) {
         if (pc.getRegistry().containsBeanDefinition(BeanIds.FILTER_CHAIN_PROXY)) {
             // Already registered. Obtain the filter chain map and add the new entries to it
-            // pc.getReaderContext().error("Duplicate <http> element detected", source);
 
             BeanDefinition fcp = pc.getRegistry().getBeanDefinition(BeanIds.FILTER_CHAIN_PROXY);
             Map existingFilterChainMap = (Map) fcp.getPropertyValues().getPropertyValue("filterChainMap").getValue();

+ 3 - 0
config/src/main/resources/org/springframework/security/config/spring-security-3.1.rnc

@@ -257,6 +257,9 @@ protect-pointcut.attlist &=
     ## Access configuration attributes list that applies to all methods matching the pointcut, e.g. "ROLE_A,ROLE_B"
     attribute access {xsd:token}
 
+http-firewall =
+    ## Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created by the namespace.
+    element http-firewall {ref}
 
 http =
     ## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the "secured" attribute to "false".

+ 5 - 0
config/src/main/resources/org/springframework/security/config/spring-security-3.1.xsd

@@ -589,6 +589,11 @@
       </xs:annotation>
     </xs:attribute>
   </xs:attributeGroup>
+  <xs:element name="http-firewall"><xs:annotation>
+      <xs:documentation>Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created by the namespace.</xs:documentation>
+    </xs:annotation><xs:complexType>
+      <xs:attributeGroup ref="security:ref"/>
+    </xs:complexType></xs:element>
   <xs:element name="http"><xs:annotation>
       <xs:documentation>Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the "secured" attribute to "false".</xs:documentation>
     </xs:annotation><xs:complexType>

+ 14 - 2
config/src/test/groovy/org/springframework/security/config/http/MiscHttpConfigTests.groovy

@@ -44,6 +44,7 @@ import org.springframework.security.web.savedrequest.RequestCacheAwareFilter
 import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
 import org.springframework.security.web.session.SessionManagementFilter
 import org.springframework.security.web.authentication.logout.CookieClearingLogoutHandler
+import org.springframework.security.web.firewall.DefaultHttpFirewall
 
 class MiscHttpConfigTests extends AbstractHttpConfigTests {
     def 'Minimal configuration parses'() {
@@ -582,13 +583,24 @@ class MiscHttpConfigTests extends AbstractHttpConfigTests {
         getFilter(BasicAuthenticationFilter).authenticationDetailsSource == adsr
         getFilter(X509AuthenticationFilter).authenticationDetailsSource == adsr
     }
-    
+
     def includeJaasApiIntegrationFilter() {
         xml.http(['auto-config':'true','jaas-api-provision':'true'])
         createAppContext()
         expect:
         getFilter(JaasApiIntegrationFilter.class) != null
-        
+    }
+
+    def httpFirewallInjectionIsSupported() {
+        xml.'http-firewall'(ref: 'fw')
+        xml.http() {
+           'form-login'()
+        }
+        bean('fw', DefaultHttpFirewall)
+        createAppContext()
+        FilterChainProxy fcp = appContext.getBean(BeanIds.FILTER_CHAIN_PROXY)
+        expect:
+        fcp.firewall == appContext.getBean('fw')
     }
 }
 

+ 8 - 1
docs/manual/src/docbook/appendix-namespace.xml

@@ -524,12 +524,19 @@
                 chapter.</para>
         </section>
         <section xml:id="nsa-request-cache">
-            <title>The <literal>request-cache</literal> Element</title>
+            <title>The <literal>&lt;request-cache></literal> Element</title>
             <para>Sets the <interfacename>RequestCache</interfacename> instance which will be used
                 by the <classname>ExceptionTranslationFilter</classname> to store request
                 information before invoking an
                 <interfacename>AuthenticationEntryPoint</interfacename>. </para>
         </section>
+        <section>
+            <title>The <literal>&lt;http-firewall></literal> Element</title>
+            <para>This is a top-level element which can be used to inject a custom implementation of
+                <interfacename>HttpFirewall</interfacename> into the
+                <classname>FilterChainProxy</classname> created by the namespace. The default
+                implementation should be suitable for most applications.</para>
+        </section>
     </section>
     <section xml:id="nsa-authentication">
         <title>Authentication Services</title>