diff --git a/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java b/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java index bed550efa9..c4fe9acd80 100644 --- a/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java +++ b/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java @@ -1,27 +1,39 @@ package org.springframework.security.config; -import org.springframework.beans.factory.config.BeanDefinition; -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.xml.BeanDefinitionParser; -import org.springframework.beans.factory.xml.ParserContext; import org.springframework.security.ConfigAttributeDefinition; import org.springframework.security.ConfigAttributeEditor; import org.springframework.security.context.HttpSessionContextIntegrationFilter; +import org.springframework.security.intercept.web.AbstractFilterInvocationDefinitionSource; import org.springframework.security.intercept.web.FilterInvocationDefinitionMap; import org.springframework.security.intercept.web.FilterSecurityInterceptor; import org.springframework.security.intercept.web.PathBasedFilterInvocationDefinitionMap; import org.springframework.security.intercept.web.RegExpBasedFilterInvocationDefinitionMap; +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.ui.ExceptionTranslationFilter; import org.springframework.security.util.FilterChainProxy; import org.springframework.security.util.RegexUrlPathMatcher; +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.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.beans.factory.xml.BeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; + import org.w3c.dom.Element; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; /** * Sets up HTTP security: filter stack and protected URLs. @@ -38,6 +50,8 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { public static final String DEFAULT_LOGOUT_FILTER_ID = "_logoutFilter"; public static final String DEFAULT_EXCEPTION_TRANSLATION_FILTER_ID = "_exceptionTranslationFilter"; public static final String DEFAULT_FILTER_SECURITY_INTERCEPTOR_ID = "_filterSecurityInterceptor"; + public static final String DEFAULT_CHANNEL_PROCESSING_FILTER_ID = "_channelProcessingFilter"; + public static final String DEFAULT_CHANNEL_DECISION_MANAGER_ID = "_channelDecisionManager"; public static final String CONCURRENT_SESSIONS_ELEMENT = "concurrent-session-control"; public static final String LOGOUT_ELEMENT = "logout"; @@ -53,6 +67,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { static final String NO_FILTERS_VALUE = "none"; private static final String ACCESS_CONFIG_ATTRIBUTE = "access"; + private static final String REQUIRES_CHANNEL_ATTRIBUTE = "requiresChannel"; public BeanDefinition parse(Element element, ParserContext parserContext) { RootBeanDefinition filterChainProxy = new RootBeanDefinition(FilterChainProxy.class); @@ -78,10 +93,12 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { String patternType = element.getAttribute(PATTERN_TYPE_ATTRIBUTE); FilterInvocationDefinitionMap interceptorFilterInvDefSource = new PathBasedFilterInvocationDefinitionMap(); + FilterInvocationDefinitionMap channelFilterInvDefSource = new PathBasedFilterInvocationDefinitionMap(); if (patternType.equals(PATTERN_TYPE_REGEX)) { filterChainProxy.getPropertyValues().addPropertyValue("matcher", new RegexUrlPathMatcher()); interceptorFilterInvDefSource = new RegExpBasedFilterInvocationDefinitionMap(); + channelFilterInvDefSource = new RegExpBasedFilterInvocationDefinitionMap(); } filterChainProxy.getPropertyValues().addPropertyValue("filterChainMap", filterChainMap); @@ -92,7 +109,28 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { //filterSecurityInterceptorBuilder.setAutowireMode(RootBeanDefinition.AUTOWIRE_BY_TYPE); parseInterceptUrls(DomUtils.getChildElementsByTagName(element, "intercept-url"), - filterChainMap, interceptorFilterInvDefSource); + 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 + RootBeanDefinition channelFilter = new RootBeanDefinition(ChannelProcessingFilter.class); + channelFilter.getPropertyValues().addPropertyValue("channelDecisionManager", + new RuntimeBeanReference(DEFAULT_CHANNEL_DECISION_MANAGER_ID)); + + channelFilter.getPropertyValues().addPropertyValue("filterInvocationDefinitionSource", + channelFilterInvDefSource); + RootBeanDefinition channelDecisionManager = new RootBeanDefinition(ChannelDecisionManagerImpl.class); + List channelProcessors = new ArrayList(2); + channelProcessors.add(new SecureChannelProcessor()); + channelProcessors.add(new InsecureChannelProcessor()); + channelDecisionManager.getPropertyValues().addPropertyValue("channelProcessors", channelProcessors); + + registry.registerBeanDefinition(DEFAULT_CHANNEL_PROCESSING_FILTER_ID, channelFilter); + registry.registerBeanDefinition(DEFAULT_CHANNEL_DECISION_MANAGER_ID, channelDecisionManager); + } Element sessionControlElt = DomUtils.getChildElementByTagName(element, CONCURRENT_SESSIONS_ELEMENT); @@ -100,8 +138,8 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { new ConcurrentSessionsBeanDefinitionParser().parse(sessionControlElt, parserContext); } - // Parse remember me before logout as RememberMeServices is also a LogoutHandler implementation. - BeanDefinitionRegistry registry = parserContext.getRegistry(); + // Parse remember me before logout as RememberMeServices is also a LogoutHandler implementation. + Element rememberMeElt = DomUtils.getChildElementByTagName(element, REMEMBER_ME_ELEMENT); @@ -149,11 +187,12 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { * FilterInvocationDefinitionSource used in FilterSecurityInterceptor. */ private void parseInterceptUrls(List urlElts, Map filterChainMap, - FilterInvocationDefinitionMap interceptorFilterInvDefSource) { + FilterInvocationDefinitionMap interceptorFilterInvDefSource, + FilterInvocationDefinitionMap channelFilterInvDefSource, ParserContext parserContext) { Iterator urlEltsIterator = urlElts.iterator(); - ConfigAttributeEditor attributeEditor = new ConfigAttributeEditor(); + ConfigAttributeEditor editor = new ConfigAttributeEditor(); while (urlEltsIterator.hasNext()) { Element urlElt = (Element) urlEltsIterator.next(); @@ -166,18 +205,33 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { // Convert the comma-separated list of access attributes to a ConfigAttributeDefinition if (StringUtils.hasText(access)) { - attributeEditor.setAsText(access); + editor.setAsText(access); + interceptorFilterInvDefSource.addSecureUrl(path, (ConfigAttributeDefinition) editor.getValue()); + } + + String requiredChannel = urlElt.getAttribute(REQUIRES_CHANNEL_ATTRIBUTE); - ConfigAttributeDefinition attributeDef = (ConfigAttributeDefinition) attributeEditor.getValue(); + if (StringUtils.hasText(requiredChannel)) { + String channelConfigAttribute = null; + + if (requiredChannel.equals("https")) { + channelConfigAttribute = "REQUIRES_SECURE_CHANNEL"; + } else if (requiredChannel.equals("http")) { + channelConfigAttribute = "REQUIRES_INSECURE_CHANNEL"; + } else { + parserContext.getReaderContext().error("Unsupported channel " + requiredChannel, urlElt); + } - interceptorFilterInvDefSource.addSecureUrl(path, attributeDef); + editor.setAsText(channelConfigAttribute); + channelFilterInvDefSource.addSecureUrl(path, (ConfigAttributeDefinition) editor.getValue()); } String filters = urlElt.getAttribute(FILTERS_ATTRIBUTE); if (StringUtils.hasText(filters)) { if (!filters.equals(NO_FILTERS_VALUE)) { - throw new IllegalStateException("Currently only 'none' is supported as the custom filters attribute"); + parserContext.getReaderContext().error("Currently only 'none' is supported as the custom " + + "filters attribute", urlElt); } filterChainMap.put(path, Collections.EMPTY_LIST); diff --git a/core/src/main/java/org/springframework/security/config/HttpSecurityConfigPostProcessor.java b/core/src/main/java/org/springframework/security/config/HttpSecurityConfigPostProcessor.java index 3e254f26a5..649ff8f037 100644 --- a/core/src/main/java/org/springframework/security/config/HttpSecurityConfigPostProcessor.java +++ b/core/src/main/java/org/springframework/security/config/HttpSecurityConfigPostProcessor.java @@ -163,7 +163,7 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor if (!(filter instanceof Ordered)) { // TODO: Possibly log this as a warning and skip this filter. - throw new IllegalArgumentException("Filter " + id + " must implement the Ordered interface"); + throw new SecurityConfigurationException("Filter " + id + " must implement the Ordered interface"); } orderedFilters.add(filter); diff --git a/core/src/main/java/org/springframework/security/securechannel/ChannelProcessingFilter.java b/core/src/main/java/org/springframework/security/securechannel/ChannelProcessingFilter.java index 5f44c7189e..47b489615b 100644 --- a/core/src/main/java/org/springframework/security/securechannel/ChannelProcessingFilter.java +++ b/core/src/main/java/org/springframework/security/securechannel/ChannelProcessingFilter.java @@ -17,47 +17,40 @@ package org.springframework.security.securechannel; import org.springframework.security.ConfigAttribute; import org.springframework.security.ConfigAttributeDefinition; - import org.springframework.security.intercept.web.FilterInvocation; import org.springframework.security.intercept.web.FilterInvocationDefinitionSource; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - +import org.springframework.security.ui.SpringSecurityFilter; +import org.springframework.security.ui.FilterChainOrderUtils; import org.springframework.beans.factory.InitializingBean; - import org.springframework.util.Assert; -import java.io.IOException; - -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; -import javax.servlet.Filter; import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; /** - * Ensures a web request is delivered over the required channel.
Internally uses a {@link FilterInvocation} to
- * represent the request, so that the FilterInvocation-related property editors and lookup classes can be
- * used.
Delegates the actual channel security decisions and necessary actions to the configured {@link
- * ChannelDecisionManager}. If a response is committed by the ChannelDecisionManager, the filter chain
- * will not proceed.
Do not use this class directly. Instead configure web.xml to use the {@link
+ * Ensures a web request is delivered over the required channel.
+ *
Internally uses a {@link FilterInvocation} to represent the request, so that the
+ * FilterInvocation-related property editors and lookup classes can be used.
Delegates the actual channel security decisions and necessary actions to the configured
+ * {@link ChannelDecisionManager}. If a response is committed by the ChannelDecisionManager,
+ * the filter chain will not proceed.
Do not use this class directly. Instead configure web.xml to use the {@link
* org.springframework.security.util.FilterToBeanProxy}.