Add RedirectToHttps to XML

Closes gh-16775
This commit is contained in:
Josh Cummings 2025-03-19 15:25:29 -06:00
parent 989aee244b
commit e6008b6067
No known key found for this signature in database
GPG Key ID: 869B37A20E876129
8 changed files with 94 additions and 4 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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