Przeglądaj źródła

SEC-628: Added port-mappings element to allow use of a PortMapper.

Luke Taylor 17 lat temu
rodzic
commit
9e21c48fce

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

@@ -43,4 +43,5 @@ public abstract class BeanIds {
 	public static final String METHOD_DEFINITION_ATTRIBUTES = "_methodDefinitionAttributes";
     public static final String EMBEDDED_APACHE_DS = "_apacheDirectoryServerContainer";
     public static final String CONTEXT_SOURCE = "_securityContextSource";
+    public static final String PORT_MAPPER = "_portMapper";    
 }

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

@@ -27,4 +27,6 @@ abstract class Elements {
 	public static final String ANNOTATION_DRIVEN = "annotation-driven";
 	public static final String PASSWORD_ENCODER = "password-encoder";
 	public static final String SALT_SOURCE = "salt-source";
+	public static final String PORT_MAPPINGS = "port-mappings";
+    public static final String PORT_MAPPING = "port-mapping";    
 }

+ 21 - 6
core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java

@@ -1,6 +1,5 @@
 package org.springframework.security.config;
 
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
@@ -12,6 +11,7 @@ import org.springframework.beans.factory.config.RuntimeBeanReference;
 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
 import org.springframework.beans.factory.support.BeanDefinitionRegistry;
 import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.beans.factory.support.ManagedList;
 import org.springframework.beans.factory.xml.BeanDefinitionParser;
 import org.springframework.beans.factory.xml.ParserContext;
 import org.springframework.security.ConfigAttributeDefinition;
@@ -27,6 +27,8 @@ import org.springframework.security.securechannel.ChannelDecisionManagerImpl;
 import org.springframework.security.securechannel.ChannelProcessingFilter;
 import org.springframework.security.securechannel.InsecureChannelProcessor;
 import org.springframework.security.securechannel.SecureChannelProcessor;
+import org.springframework.security.securechannel.RetryWithHttpEntryPoint;
+import org.springframework.security.securechannel.RetryWithHttpsEntryPoint;
 import org.springframework.security.ui.ExceptionTranslationFilter;
 import org.springframework.security.util.FilterChainProxy;
 import org.springframework.security.util.RegexUrlPathMatcher;
@@ -76,9 +78,16 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
     static final String ATT_ACCESS_MGR = "access-decision-manager";
 
     public BeanDefinition parse(Element element, ParserContext parserContext) {
+        BeanDefinitionRegistry registry = parserContext.getRegistry();
         RootBeanDefinition filterChainProxy = new RootBeanDefinition(FilterChainProxy.class);
         RootBeanDefinition httpScif = new RootBeanDefinition(HttpSessionContextIntegrationFilter.class);
 
+        BeanDefinition portMapper = new PortMappingsBeanDefinitionParser().parse(
+                DomUtils.getChildElementByTagName(element, Elements.PORT_MAPPINGS), parserContext);
+        registry.registerBeanDefinition(BeanIds.PORT_MAPPER, portMapper);
+
+        RuntimeBeanReference portMapperRef = new RuntimeBeanReference(BeanIds.PORT_MAPPER);
+
         String createSession = element.getAttribute(ATT_CREATE_SESSION);
         if (OPT_CREATE_SESSION_ALWAYS.equals(createSession)) {
         	httpScif.getPropertyValues().addPropertyValue("allowSessionCreation", Boolean.TRUE);
@@ -157,8 +166,6 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
         parseInterceptUrls(DomUtils.getChildElementsByTagName(element, "intercept-url"),
                 filterChainMap, interceptorFilterInvDefSource, channelFilterInvDefSource, parserContext);
 
-        BeanDefinitionRegistry registry = parserContext.getRegistry();
-
         // Check if we need to register the channel processing beans
         if (((AbstractFilterInvocationDefinitionSource)channelFilterInvDefSource).getMapSize() > 0) {
             // At least one channel requirement has been specified
@@ -169,9 +176,17 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
             channelFilter.getPropertyValues().addPropertyValue("filterInvocationDefinitionSource",
                     channelFilterInvDefSource);
             RootBeanDefinition channelDecisionManager = new RootBeanDefinition(ChannelDecisionManagerImpl.class);
-            List channelProcessors = new ArrayList(2);
-            channelProcessors.add(new SecureChannelProcessor());
-            channelProcessors.add(new InsecureChannelProcessor());
+            ManagedList channelProcessors = new ManagedList(2);
+            RootBeanDefinition secureChannelProcessor = new RootBeanDefinition(SecureChannelProcessor.class);
+            RootBeanDefinition retryWithHttp = new RootBeanDefinition(RetryWithHttpEntryPoint.class);
+            RootBeanDefinition retryWithHttps = new RootBeanDefinition(RetryWithHttpsEntryPoint.class);
+            retryWithHttp.getPropertyValues().addPropertyValue("portMapper", portMapperRef);
+            retryWithHttps.getPropertyValues().addPropertyValue("portMapper", portMapperRef);            
+            secureChannelProcessor.getPropertyValues().addPropertyValue("entryPoint", retryWithHttps);
+            RootBeanDefinition inSecureChannelProcessor = new RootBeanDefinition(InsecureChannelProcessor.class);
+            inSecureChannelProcessor.getPropertyValues().addPropertyValue("entryPoint", retryWithHttp);
+            channelProcessors.add(secureChannelProcessor);
+            channelProcessors.add(inSecureChannelProcessor);
             channelDecisionManager.getPropertyValues().addPropertyValue("channelProcessors", channelProcessors);
 
             registry.registerBeanDefinition(BeanIds.CHANNEL_PROCESSING_FILTER, channelFilter);

+ 53 - 0
core/src/main/java/org/springframework/security/config/PortMappingsBeanDefinitionParser.java

@@ -0,0 +1,53 @@
+package org.springframework.security.config;
+
+import org.springframework.security.util.PortMapperImpl;
+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.support.RootBeanDefinition;
+import org.springframework.util.Assert;
+import org.springframework.util.xml.DomUtils;
+
+import org.w3c.dom.Element;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Parses a port-mappings element, producing a single {@link org.springframework.security.util.PortMapperImpl}
+ * bean.
+ *
+ * @author Luke Taylor
+ * @version $Id$
+ */
+public class PortMappingsBeanDefinitionParser implements BeanDefinitionParser {
+    public static final String ATT_HTTP_PORT = "http";
+    public static final String ATT_HTTPS_PORT = "https";
+
+    public BeanDefinition parse(Element element, ParserContext parserContext) {
+        BeanDefinition portMapper = new RootBeanDefinition(PortMapperImpl.class);
+
+        if (element != null) {
+            List mappingElts = DomUtils.getChildElementsByTagName(element, Elements.PORT_MAPPING);    
+            Assert.notEmpty(mappingElts, "No port-mapping child elements!");
+            Map mappings = new HashMap();
+
+            Iterator iterator = mappingElts.iterator();
+            while (iterator.hasNext()) {
+                Element elt = (Element) iterator.next();
+                String httpPort = elt.getAttribute(ATT_HTTP_PORT);
+                String httpsPort = elt.getAttribute(ATT_HTTPS_PORT);
+                Assert.notNull(httpPort, "No http port supplied in mapping");
+                Assert.notNull(httpsPort, "No https port supplied in mapping");
+
+                mappings.put(httpPort, httpsPort);
+            }
+
+            portMapper.getPropertyValues().addPropertyValue("portMappings", mappings);
+        }
+
+        return portMapper;
+    }
+}

+ 13 - 7
core/src/main/java/org/springframework/security/util/PortMapperImpl.java

@@ -23,9 +23,10 @@ import java.util.Map;
 
 
 /**
- * Concrete implementation of {@link PortMapper} that obtains HTTP:HTTPS pairs from the application context.<P>By
- * default the implementation will assume 80:443 and 8080:8443 are HTTP:HTTPS pairs respectively. If different pairs
- * are required, use {@link #setPortMappings(Map)}.</p>
+ * Concrete implementation of {@link PortMapper} that obtains HTTP:HTTPS pairs from the application context.
+ * <p>
+ * By default the implementation will assume 80:443 and 8080:8443 are HTTP:HTTPS pairs respectively. If different pairs
+ * are required, use {@link #setPortMappings(Map)}.
  *
  * @author Ben Alex
  * @author colin sampaleanu
@@ -75,10 +76,15 @@ public class PortMapperImpl implements PortMapper {
     }
 
     /**
-     * <p>Set to override the default HTTP port to HTTPS port mappings of 80:443, and  8080:8443.</p>
-     *  In a Spring XML ApplicationContext, a definition would look something like this:<pre>
-     *   &lt;property name="portMappings">    &lt;map>      &lt;entry key="80">&lt;value>443&lt;/value>&lt;/entry>
-     *       &lt;entry key="8080">&lt;value>8443&lt;/value>&lt;/entry>    &lt;/map>  &lt;/property></pre>
+     * Set to override the default HTTP port to HTTPS port mappings of 80:443, and  8080:8443.
+     * In a Spring XML ApplicationContext, a definition would look something like this:
+     * <pre>
+     *  &lt;property name="portMappings">
+     *      &lt;map>
+     *          &lt;entry key="80">&lt;value>443&lt;/value>&lt;/entry>
+     *          &lt;entry key="8080">&lt;value>8443&lt;/value>&lt;/entry>
+     *      &lt;/map>
+     * &lt;/property></pre>
      *
      * @param newMappings A Map consisting of String keys and String values, where for each entry the key is the string
      *        representation of an integer HTTP port number, and the value is the string representation of the

+ 13 - 1
core/src/main/resources/org/springframework/security/config/spring-security-2.0.rnc

@@ -97,7 +97,7 @@ annotation-driven.attlist &=
 
 http =
     ## Container element for HTTP security configuration
-   element http {http.attlist, (intercept-url+ & form-login? & http-basic? & logout? & concurrent-session-control? & remember-me? & anonymous?) }
+   element http {http.attlist, (intercept-url+ & form-login? & http-basic? & logout? & concurrent-session-control? & remember-me? & anonymous? & port-mappings) }
 http.attlist &=
     ## Automatically registers a login form, BASIC authentication, anonymous authentication, logout services, remember-me and servlet-api-integration. If set to "true", all of these capabilities are added (although you can still customize the configuration of each by providing the respective element). If unspecified, defaults to "false".
     attribute auto-config {"true" | "false" }?
@@ -239,6 +239,18 @@ user.attlist &=
 	## One of more authorities granted to the user. Separate authorities with a comma (but no space). For example, "ROLE_USER,ROLE_ADMINISTRATOR"
     attribute authorities {xsd:string}
 
+port-mappings = 
+    ## Defines the list of mappings between http and https ports for use in redirects
+    element port-mappings {port-mappings.attlist, port-mapping+}
+
+port-mappings.attlist &= empty
+
+port-mapping = 
+    element port-mapping {http-port, https-port}
+    
+http-port = attribute http {xsd:integer}
+
+https-port = attribute https {xsd:integer}
 
 jdbc-user-service =
 	## Causes creation of a JDBC-based UserDetailsService.

+ 23 - 0
core/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd

@@ -258,6 +258,7 @@
         <xs:element ref="security:concurrent-session-control"/>
         <xs:element ref="security:remember-me"/>
         <xs:element ref="security:anonymous"/>
+        <xs:element ref="security:port-mappings"/>
       </xs:choice>
       <xs:attributeGroup ref="security:http.attlist"/>
     </xs:complexType>
@@ -585,6 +586,28 @@
       </xs:annotation>
     </xs:attribute>
   </xs:attributeGroup>
+  <xs:element name="port-mappings">
+    <xs:annotation>
+      <xs:documentation>Defines the list of mappings between http and https ports for use in redirects</xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element maxOccurs="unbounded" ref="security:port-mapping"/>
+      </xs:sequence>
+    </xs:complexType>
+  </xs:element>
+  <xs:element name="port-mapping">
+    <xs:complexType>
+      <xs:attributeGroup ref="security:http-port"/>
+      <xs:attributeGroup ref="security:https-port"/>
+    </xs:complexType>
+  </xs:element>
+  <xs:attributeGroup name="http-port">
+    <xs:attribute name="http" use="required" type="xs:integer"/>
+  </xs:attributeGroup>
+  <xs:attributeGroup name="https-port">
+    <xs:attribute name="https" use="required" type="xs:integer"/>
+  </xs:attributeGroup>
   <xs:element name="jdbc-user-service">
     <xs:annotation>
       <xs:documentation>Causes creation of a JDBC-based UserDetailsService.</xs:documentation>

+ 15 - 0
core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java

@@ -4,6 +4,10 @@ import org.springframework.security.concurrent.ConcurrentSessionFilter;
 import org.springframework.security.context.HttpSessionContextIntegrationFilter;
 import org.springframework.security.intercept.web.FilterSecurityInterceptor;
 import org.springframework.security.securechannel.ChannelProcessingFilter;
+import org.springframework.security.securechannel.ChannelDecisionManager;
+import org.springframework.security.securechannel.ChannelDecisionManagerImpl;
+import org.springframework.security.securechannel.SecureChannelProcessor;
+import org.springframework.security.securechannel.RetryWithHttpsEntryPoint;
 import org.springframework.security.ui.ExceptionTranslationFilter;
 import org.springframework.security.ui.basicauth.BasicProcessingFilter;
 import org.springframework.security.ui.logout.LogoutFilter;
@@ -11,9 +15,11 @@ import org.springframework.security.ui.rememberme.RememberMeProcessingFilter;
 import org.springframework.security.ui.webapp.AuthenticationProcessingFilter;
 import org.springframework.security.ui.webapp.DefaultLoginPageGeneratingFilter;
 import org.springframework.security.util.FilterChainProxy;
+import org.springframework.security.util.PortMapperImpl;
 import org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter;
 import org.springframework.context.support.ClassPathXmlApplicationContext;
 import org.springframework.beans.BeansException;
+import org.springframework.util.ReflectionUtils;
 
 import org.junit.AfterClass;
 import static org.junit.Assert.assertEquals;
@@ -79,5 +85,14 @@ public class HttpSecurityBeanDefinitionParserTests {
         assertTrue(filters.next() instanceof RememberMeProcessingFilter);
         assertTrue(filters.next() instanceof ExceptionTranslationFilter);
         assertTrue(filters.next() instanceof FilterSecurityInterceptor);
+
+    }
+
+    @Test
+    public void portMappingsAreParsedCorrectly() throws Exception {
+        PortMapperImpl pm = (PortMapperImpl) appContext.getBean(BeanIds.PORT_MAPPER);
+        assertEquals(1, pm.getTranslatedPortMappings().size());
+        assertEquals(Integer.valueOf(9080), pm.lookupHttpPort(9443));
+        assertEquals(Integer.valueOf(9443), pm.lookupHttpsPort(9080));
     }
 }

+ 4 - 0
core/src/test/resources/org/springframework/security/config/http-security.xml

@@ -23,6 +23,10 @@ http://www.springframework.org/schema/security http://www.springframework.org/sc
         <concurrent-session-control max-sessions="1"/>
 
         <remember-me key="doesntmatter" token-repository="tokenRepo"/>
+
+        <port-mappings>
+            <port-mapping http="9080" https="9443"/>
+        </port-mappings>
     </http>
 
     <authentication-provider>