2
0
Эх сурвалжийг харах

Add RedirectToHttps to XML

Closes gh-16775
Josh Cummings 5 сар өмнө
parent
commit
e6008b6067

+ 23 - 1
config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright 2002-2024 the original author or authors.
+ * Copyright 2002-2025 the original author or authors.
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -82,6 +82,7 @@ import org.springframework.security.web.session.ForceEagerSessionCreationFilter;
 import org.springframework.security.web.session.SessionManagementFilter;
 import org.springframework.security.web.session.SessionManagementFilter;
 import org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy;
 import org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy;
 import org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy;
 import org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy;
+import org.springframework.security.web.transport.HttpsRedirectFilter;
 import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
 import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
 import org.springframework.util.Assert;
 import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.ClassUtils;
@@ -176,6 +177,8 @@ class HttpConfigurationBuilder {
 
 
 	private BeanDefinition cpf;
 	private BeanDefinition cpf;
 
 
+	private BeanDefinition httpsRedirectFilter;
+
 	private BeanDefinition securityContextPersistenceFilter;
 	private BeanDefinition securityContextPersistenceFilter;
 
 
 	private BeanDefinition forceEagerSessionCreationFilter;
 	private BeanDefinition forceEagerSessionCreationFilter;
@@ -252,6 +255,7 @@ class HttpConfigurationBuilder {
 		createServletApiFilter(authenticationManager);
 		createServletApiFilter(authenticationManager);
 		createJaasApiFilter();
 		createJaasApiFilter();
 		createChannelProcessingFilter();
 		createChannelProcessingFilter();
+		createHttpsRedirectFilter();
 		createFilterSecurity(authenticationManager);
 		createFilterSecurity(authenticationManager);
 		createAddHeadersFilter();
 		createAddHeadersFilter();
 		createCorsFilter();
 		createCorsFilter();
@@ -656,6 +660,19 @@ class HttpConfigurationBuilder {
 		}
 		}
 	}
 	}
 
 
+	private void createHttpsRedirectFilter() {
+		String ref = this.httpElt
+			.getAttribute(HttpSecurityBeanDefinitionParser.ATT_REDIRECT_TO_HTTPS_REQUEST_MATCHER_REF);
+		if (!StringUtils.hasText(ref)) {
+			return;
+		}
+		RootBeanDefinition channelFilter = new RootBeanDefinition(HttpsRedirectFilter.class);
+		channelFilter.getPropertyValues().addPropertyValue("requestMatcher", new RuntimeBeanReference(ref));
+		channelFilter.getPropertyValues().addPropertyValue("portMapper", this.portMapper);
+		this.httpsRedirectFilter = channelFilter;
+	}
+
+	@Deprecated
 	private void createChannelProcessingFilter() {
 	private void createChannelProcessingFilter() {
 		ManagedMap<BeanMetadataElement, BeanDefinition> channelRequestMap = parseInterceptUrlsForChannelSecurity();
 		ManagedMap<BeanMetadataElement, BeanDefinition> channelRequestMap = parseInterceptUrlsForChannelSecurity();
 		if (channelRequestMap.isEmpty()) {
 		if (channelRequestMap.isEmpty()) {
@@ -691,7 +708,9 @@ class HttpConfigurationBuilder {
 	 * Parses the intercept-url elements to obtain the map used by channel security. This
 	 * Parses the intercept-url elements to obtain the map used by channel security. This
 	 * will be empty unless the <tt>requires-channel</tt> attribute has been used on a URL
 	 * will be empty unless the <tt>requires-channel</tt> attribute has been used on a URL
 	 * path.
 	 * path.
+	 * @deprecated please use {@link #createHttpsRedirectFilter} instead
 	 */
 	 */
+	@Deprecated
 	private ManagedMap<BeanMetadataElement, BeanDefinition> parseInterceptUrlsForChannelSecurity() {
 	private ManagedMap<BeanMetadataElement, BeanDefinition> parseInterceptUrlsForChannelSecurity() {
 		ManagedMap<BeanMetadataElement, BeanDefinition> channelRequestMap = new ManagedMap<>();
 		ManagedMap<BeanMetadataElement, BeanDefinition> channelRequestMap = new ManagedMap<>();
 		for (Element urlElt : this.interceptUrls) {
 		for (Element urlElt : this.interceptUrls) {
@@ -897,6 +916,9 @@ class HttpConfigurationBuilder {
 		if (this.disableUrlRewriteFilter != null) {
 		if (this.disableUrlRewriteFilter != null) {
 			filters.add(new OrderDecorator(this.disableUrlRewriteFilter, SecurityFilters.DISABLE_ENCODE_URL_FILTER));
 			filters.add(new OrderDecorator(this.disableUrlRewriteFilter, SecurityFilters.DISABLE_ENCODE_URL_FILTER));
 		}
 		}
+		if (this.httpsRedirectFilter != null) {
+			filters.add(new OrderDecorator(this.httpsRedirectFilter, SecurityFilters.HTTPS_REDIRECT_FILTER));
+		}
 		if (this.cpf != null) {
 		if (this.cpf != null) {
 			filters.add(new OrderDecorator(this.cpf, SecurityFilters.CHANNEL_FILTER));
 			filters.add(new OrderDecorator(this.cpf, SecurityFilters.CHANNEL_FILTER));
 		}
 		}

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

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2025 the original author or authors.
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -81,6 +81,8 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
 
 
 	static final String ATT_REQUEST_MATCHER_REF = "request-matcher-ref";
 	static final String ATT_REQUEST_MATCHER_REF = "request-matcher-ref";
 
 
+	static final String ATT_REDIRECT_TO_HTTPS_REQUEST_MATCHER_REF = "redirect-to-https-request-matcher-ref";
+
 	static final String ATT_PATH_PATTERN = "pattern";
 	static final String ATT_PATH_PATTERN = "pattern";
 
 
 	static final String ATT_HTTP_METHOD = "method";
 	static final String ATT_HTTP_METHOD = "method";

+ 4 - 1
config/src/main/java/org/springframework/security/config/http/SecurityFilters.java

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2025 the original author or authors.
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -33,8 +33,11 @@ enum SecurityFilters {
 
 
 	FORCE_EAGER_SESSION_FILTER,
 	FORCE_EAGER_SESSION_FILTER,
 
 
+	@Deprecated
 	CHANNEL_FILTER,
 	CHANNEL_FILTER,
 
 
+	HTTPS_REDIRECT_FILTER,
+
 	SECURITY_CONTEXT_FILTER,
 	SECURITY_CONTEXT_FILTER,
 
 
 	CONCURRENT_SESSION_FILTER,
 	CONCURRENT_SESSION_FILTER,

+ 2 - 0
config/src/main/resources/org/springframework/security/config/spring-security-6.5.rnc

@@ -345,6 +345,8 @@ http.attlist &=
 http.attlist &=
 http.attlist &=
 	## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.
 	## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.
 	attribute request-matcher-ref { xsd:token }?
 	attribute request-matcher-ref { xsd:token }?
+http.attlist &=
+    attribute redirect-to-https-request-matcher-ref { xsd:token }?
 http.attlist &=
 http.attlist &=
 	## A legacy attribute which automatically registers a login form, BASIC authentication and a logout URL and logout services. If unspecified, defaults to "false". We'd recommend you avoid using this and instead explicitly configure the services you require.
 	## A legacy attribute which automatically registers a login form, BASIC authentication and a logout URL and logout services. If unspecified, defaults to "false". We'd recommend you avoid using this and instead explicitly configure the services you require.
 	attribute auto-config {xsd:boolean}?
 	attribute auto-config {xsd:boolean}?

+ 1 - 0
config/src/main/resources/org/springframework/security/config/spring-security-6.5.xsd

@@ -1242,6 +1242,7 @@
                 </xs:documentation>
                 </xs:documentation>
          </xs:annotation>
          </xs:annotation>
       </xs:attribute>
       </xs:attribute>
+      <xs:attribute name="redirect-to-https-request-matcher-ref" type="xs:token"/>
       <xs:attribute name="auto-config" type="xs:boolean">
       <xs:attribute name="auto-config" type="xs:boolean">
          <xs:annotation>
          <xs:annotation>
             <xs:documentation>A legacy attribute which automatically registers a login form, BASIC authentication and a
             <xs:documentation>A legacy attribute which automatically registers a login form, BASIC authentication and a

+ 17 - 1
config/src/test/java/org/springframework/security/config/http/MiscHttpConfigTests.java

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2025 the original author or authors.
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -109,6 +109,7 @@ import org.springframework.security.web.savedrequest.RequestCache;
 import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
 import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
 import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
 import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
 import org.springframework.security.web.session.DisableEncodeUrlFilter;
 import org.springframework.security.web.session.DisableEncodeUrlFilter;
+import org.springframework.security.web.transport.HttpsRedirectFilter;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.MvcResult;
 import org.springframework.test.web.servlet.MvcResult;
 import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
 import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
@@ -349,6 +350,21 @@ public class MiscHttpConfigTests {
 		assertThat(getFilter(ChannelProcessingFilter.class)).isNotNull();
 		assertThat(getFilter(ChannelProcessingFilter.class)).isNotNull();
 	}
 	}
 
 
+	@Test
+	public void configureWhenRedirectToHttpsThenFilterAdded() {
+		this.spring.configLocations(xml("RedirectToHttpsRequiresHttpsAny")).autowire();
+		assertThat(getFilter(HttpsRedirectFilter.class)).isNotNull();
+	}
+
+	@Test
+	public void getWhenRedirectToHttpsAnyThenRedirects() throws Exception {
+		this.spring.configLocations(xml("RedirectToHttpsRequiresHttpsAny")).autowire();
+		// @formatter:off
+		this.mvc.perform(get("http://localhost"))
+				.andExpect(redirectedUrl("https://localhost"));
+		// @formatter:on
+	}
+
 	@Test
 	@Test
 	public void getWhenPortsMappedThenRedirectedAccordingly() throws Exception {
 	public void getWhenPortsMappedThenRedirectedAccordingly() throws Exception {
 		this.spring.configLocations(xml("PortsMappedInterceptUrlMethodRequiresAny")).autowire();
 		this.spring.configLocations(xml("PortsMappedInterceptUrlMethodRequiresAny")).autowire();

+ 40 - 0
config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-RedirectToHttpsRequiresHttpsAny.xml

@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2018 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~       https://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+		 xmlns:util="http://www.springframework.org/schema/util"
+		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		xmlns="http://www.springframework.org/schema/security"
+		xsi:schemaLocation="
+			http://www.springframework.org/schema/security
+			https://www.springframework.org/schema/security/spring-security.xsd
+			http://www.springframework.org/schema/beans
+			https://www.springframework.org/schema/beans/spring-beans.xsd
+			http://www.springframework.org/schema/util
+			https://www.springframework.org/schema/util/spring-util.xsd
+			">
+
+	<http redirect-to-https-request-matcher-ref="any">
+		<http-basic/>
+		<intercept-url pattern="/**" method="GET" access="hasRole('ADMIN')" requires-channel="https"/>
+		<intercept-url pattern="/**" access="permitAll"/>
+	</http>
+	<util:constant id="any" static-field="org.springframework.security.web.util.matcher.AnyRequestMatcher.INSTANCE" />
+
+	<b:import resource="MiscHttpConfigTests-controllers.xml"/>
+	<b:import resource="userservice.xml"/>
+</b:beans>

+ 4 - 0
docs/modules/ROOT/pages/servlet/appendix/namespace/http.adoc

@@ -118,6 +118,10 @@ If no pattern is defined, all requests will be matched, so the most specific pat
 Sets the realm name used for basic authentication (if enabled).
 Sets the realm name used for basic authentication (if enabled).
 Corresponds to the `realmName` property on `BasicAuthenticationEntryPoint`.
 Corresponds to the `realmName` property on `BasicAuthenticationEntryPoint`.
 
 
+[[nsa-redirect-to-https-request-matcher-ref]]
+* **redirect-to-https-request-matcher-ref**
+A reference to a bean that implements `RequestMatcher` that will determine which requests must redirect to HTTPS.
+This is helpful when, for example, wanting to run HTTP locally and HTTPS in production using a request header.
 
 
 [[nsa-http-request-matcher]]
 [[nsa-http-request-matcher]]
 * **request-matcher**
 * **request-matcher**