Pārlūkot izejas kodu

Add RedirectToHttps to XML

Closes gh-16775
Josh Cummings 5 mēneši atpakaļ
vecāks
revīzija
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");
  * 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.SimpleRedirectInvalidSessionStrategy;
 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.util.Assert;
 import org.springframework.util.ClassUtils;
@@ -176,6 +177,8 @@ class HttpConfigurationBuilder {
 
 	private BeanDefinition cpf;
 
+	private BeanDefinition httpsRedirectFilter;
+
 	private BeanDefinition securityContextPersistenceFilter;
 
 	private BeanDefinition forceEagerSessionCreationFilter;
@@ -252,6 +255,7 @@ class HttpConfigurationBuilder {
 		createServletApiFilter(authenticationManager);
 		createJaasApiFilter();
 		createChannelProcessingFilter();
+		createHttpsRedirectFilter();
 		createFilterSecurity(authenticationManager);
 		createAddHeadersFilter();
 		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() {
 		ManagedMap<BeanMetadataElement, BeanDefinition> channelRequestMap = parseInterceptUrlsForChannelSecurity();
 		if (channelRequestMap.isEmpty()) {
@@ -691,7 +708,9 @@ class HttpConfigurationBuilder {
 	 * 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
 	 * path.
+	 * @deprecated please use {@link #createHttpsRedirectFilter} instead
 	 */
+	@Deprecated
 	private ManagedMap<BeanMetadataElement, BeanDefinition> parseInterceptUrlsForChannelSecurity() {
 		ManagedMap<BeanMetadataElement, BeanDefinition> channelRequestMap = new ManagedMap<>();
 		for (Element urlElt : this.interceptUrls) {
@@ -897,6 +916,9 @@ class HttpConfigurationBuilder {
 		if (this.disableUrlRewriteFilter != null) {
 			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) {
 			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");
  * 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_REDIRECT_TO_HTTPS_REQUEST_MATCHER_REF = "redirect-to-https-request-matcher-ref";
+
 	static final String ATT_PATH_PATTERN = "pattern";
 
 	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");
  * you may not use this file except in compliance with the License.
@@ -33,8 +33,11 @@ enum SecurityFilters {
 
 	FORCE_EAGER_SESSION_FILTER,
 
+	@Deprecated
 	CHANNEL_FILTER,
 
+	HTTPS_REDIRECT_FILTER,
+
 	SECURITY_CONTEXT_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 &=
 	## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.
 	attribute request-matcher-ref { xsd:token }?
+http.attlist &=
+    attribute redirect-to-https-request-matcher-ref { xsd:token }?
 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.
 	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:annotation>
       </xs:attribute>
+      <xs:attribute name="redirect-to-https-request-matcher-ref" type="xs:token"/>
       <xs:attribute name="auto-config" type="xs:boolean">
          <xs:annotation>
             <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");
  * 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.servletapi.SecurityContextHolderAwareRequestFilter;
 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.MvcResult;
 import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
@@ -349,6 +350,21 @@ public class MiscHttpConfigTests {
 		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
 	public void getWhenPortsMappedThenRedirectedAccordingly() throws Exception {
 		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).
 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]]
 * **request-matcher**