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

This commit is contained in:
Luke Taylor 2008-01-15 19:59:07 +00:00
parent 60b7e2d4f2
commit 9e21c48fce
9 changed files with 145 additions and 14 deletions

View File

@ -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";
}

View File

@ -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";
}

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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

View File

@ -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.

View File

@ -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>

View File

@ -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));
}
}

View File

@ -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>