mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-03-03 11:59:08 +00:00
Add DisableUrlRewritingFilter
Closes gh-11084
This commit is contained in:
parent
32b83aae63
commit
39b0620a84
@ -42,6 +42,7 @@ import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
|
||||
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
|
||||
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
|
||||
import org.springframework.security.web.session.ConcurrentSessionFilter;
|
||||
import org.springframework.security.web.session.DisableEncodeUrlFilter;
|
||||
import org.springframework.security.web.session.SessionManagementFilter;
|
||||
|
||||
/**
|
||||
@ -124,6 +125,7 @@ public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>>
|
||||
* The ordering of the Filters is:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link DisableEncodeUrlFilter}</li>
|
||||
* <li>{@link ChannelProcessingFilter}</li>
|
||||
* <li>{@link SecurityContextPersistenceFilter}</li>
|
||||
* <li>{@link LogoutFilter}</li>
|
||||
|
@ -46,6 +46,7 @@ import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
|
||||
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
|
||||
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
|
||||
import org.springframework.security.web.session.ConcurrentSessionFilter;
|
||||
import org.springframework.security.web.session.DisableEncodeUrlFilter;
|
||||
import org.springframework.security.web.session.SessionManagementFilter;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
|
||||
@ -68,6 +69,7 @@ final class FilterOrderRegistration {
|
||||
|
||||
FilterOrderRegistration() {
|
||||
Step order = new Step(INITIAL_ORDER, ORDER_STEP);
|
||||
put(DisableEncodeUrlFilter.class, order.next());
|
||||
put(ChannelProcessingFilter.class, order.next());
|
||||
order.next(); // gh-8105
|
||||
put(WebAsyncManagerIntegrationFilter.class, order.next());
|
||||
|
@ -52,6 +52,7 @@ import org.springframework.security.web.context.SecurityContextRepository;
|
||||
import org.springframework.security.web.savedrequest.NullRequestCache;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
import org.springframework.security.web.session.ConcurrentSessionFilter;
|
||||
import org.springframework.security.web.session.DisableEncodeUrlFilter;
|
||||
import org.springframework.security.web.session.InvalidSessionStrategy;
|
||||
import org.springframework.security.web.session.SessionInformationExpiredStrategy;
|
||||
import org.springframework.security.web.session.SessionManagementFilter;
|
||||
@ -376,6 +377,9 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
concurrentSessionFilter = postProcess(concurrentSessionFilter);
|
||||
http.addFilter(concurrentSessionFilter);
|
||||
}
|
||||
if (!this.enableSessionUrlRewriting) {
|
||||
http.addFilter(new DisableEncodeUrlFilter());
|
||||
}
|
||||
}
|
||||
|
||||
private ConcurrentSessionFilter createConcurrencyFilter(H http) {
|
||||
|
@ -68,6 +68,7 @@ import org.springframework.security.web.savedrequest.NullRequestCache;
|
||||
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
|
||||
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
|
||||
import org.springframework.security.web.session.ConcurrentSessionFilter;
|
||||
import org.springframework.security.web.session.DisableEncodeUrlFilter;
|
||||
import org.springframework.security.web.session.SessionManagementFilter;
|
||||
import org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy;
|
||||
import org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy;
|
||||
@ -179,6 +180,8 @@ class HttpConfigurationBuilder {
|
||||
|
||||
private BeanDefinition csrfFilter;
|
||||
|
||||
private BeanDefinition disableUrlRewriteFilter;
|
||||
|
||||
private BeanDefinition wellKnownChangePasswordRedirectFilter;
|
||||
|
||||
private BeanMetadataElement csrfLogoutHandler;
|
||||
@ -204,6 +207,7 @@ class HttpConfigurationBuilder {
|
||||
String createSession = element.getAttribute(ATT_CREATE_SESSION);
|
||||
this.sessionPolicy = !StringUtils.hasText(createSession) ? SessionCreationPolicy.IF_REQUIRED
|
||||
: createPolicy(createSession);
|
||||
createDisableEncodeUrlFilter();
|
||||
createCsrfFilter();
|
||||
createSecurityPersistence();
|
||||
createSessionManagementFilters();
|
||||
@ -319,10 +323,6 @@ class HttpConfigurationBuilder {
|
||||
|
||||
private void createSecurityContextRepository() {
|
||||
String repoRef = this.httpElt.getAttribute(ATT_SECURITY_CONTEXT_REPOSITORY);
|
||||
String disableUrlRewriting = this.httpElt.getAttribute(ATT_DISABLE_URL_REWRITING);
|
||||
if (!StringUtils.hasText(disableUrlRewriting)) {
|
||||
disableUrlRewriting = "true";
|
||||
}
|
||||
if (!StringUtils.hasText(repoRef)) {
|
||||
BeanDefinitionBuilder contextRepo;
|
||||
if (this.sessionPolicy == SessionCreationPolicy.STATELESS) {
|
||||
@ -340,7 +340,7 @@ class HttpConfigurationBuilder {
|
||||
default:
|
||||
contextRepo.addPropertyValue("allowSessionCreation", Boolean.TRUE);
|
||||
}
|
||||
if ("true".equals(disableUrlRewriting)) {
|
||||
if (isDisableUrlRewriting()) {
|
||||
contextRepo.addPropertyValue("disableUrlRewriting", Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
@ -352,6 +352,11 @@ class HttpConfigurationBuilder {
|
||||
this.contextRepoRef = new RuntimeBeanReference(repoRef);
|
||||
}
|
||||
|
||||
private boolean isDisableUrlRewriting() {
|
||||
String disableUrlRewriting = this.httpElt.getAttribute(ATT_DISABLE_URL_REWRITING);
|
||||
return !"false".equals(disableUrlRewriting);
|
||||
}
|
||||
|
||||
private void createSecurityContextHolderFilter() {
|
||||
BeanDefinitionBuilder filter = BeanDefinitionBuilder.rootBeanDefinition(SecurityContextHolderFilter.class);
|
||||
filter.addConstructorArgValue(this.contextRepoRef);
|
||||
@ -718,6 +723,12 @@ class HttpConfigurationBuilder {
|
||||
|
||||
}
|
||||
|
||||
private void createDisableEncodeUrlFilter() {
|
||||
if (isDisableUrlRewriting()) {
|
||||
this.disableUrlRewriteFilter = new RootBeanDefinition(DisableEncodeUrlFilter.class);
|
||||
}
|
||||
}
|
||||
|
||||
private void createCsrfFilter() {
|
||||
Element elmt = DomUtils.getChildElementByTagName(this.httpElt, Elements.CSRF);
|
||||
this.csrfParser = new CsrfBeanDefinitionParser();
|
||||
@ -757,6 +768,9 @@ class HttpConfigurationBuilder {
|
||||
|
||||
List<OrderDecorator> getFilters() {
|
||||
List<OrderDecorator> filters = new ArrayList<>();
|
||||
if (this.disableUrlRewriteFilter != null) {
|
||||
filters.add(new OrderDecorator(this.disableUrlRewriteFilter, SecurityFilters.DISABLE_ENCODE_URL_FILTER));
|
||||
}
|
||||
if (this.cpf != null) {
|
||||
filters.add(new OrderDecorator(this.cpf, SecurityFilters.CHANNEL_FILTER));
|
||||
}
|
||||
|
@ -29,6 +29,8 @@ enum SecurityFilters {
|
||||
|
||||
FIRST(Integer.MIN_VALUE),
|
||||
|
||||
DISABLE_ENCODE_URL_FILTER,
|
||||
|
||||
CHANNEL_FILTER,
|
||||
|
||||
SECURITY_CONTEXT_FILTER,
|
||||
|
@ -1318,4 +1318,4 @@ position =
|
||||
## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.
|
||||
attribute position {named-security-filter}
|
||||
|
||||
named-security-filter = "FIRST" | "CHANNEL_FILTER" | "SECURITY_CONTEXT_FILTER" | "CONCURRENT_SESSION_FILTER" | "WEB_ASYNC_MANAGER_FILTER" | "HEADERS_FILTER" | "CORS_FILTER" | "SAML2_LOGOUT_REQUEST_FILTER" | "SAML2_LOGOUT_RESPONSE_FILTER" | "CSRF_FILTER" | "SAML2_LOGOUT_FILTER" | "LOGOUT_FILTER" | "OAUTH2_AUTHORIZATION_REQUEST_FILTER" | "SAML2_AUTHENTICATION_REQUEST_FILTER" | "X509_FILTER" | "PRE_AUTH_FILTER" | "CAS_FILTER" | "OAUTH2_LOGIN_FILTER" | "SAML2_AUTHENTICATION_FILTER" | "FORM_LOGIN_FILTER" | "OPENID_FILTER" | "LOGIN_PAGE_FILTER" |"LOGOUT_PAGE_FILTER" | "DIGEST_AUTH_FILTER" | "BEARER_TOKEN_AUTH_FILTER" | "BASIC_AUTH_FILTER" | "REQUEST_CACHE_FILTER" | "SERVLET_API_SUPPORT_FILTER" | "JAAS_API_SUPPORT_FILTER" | "REMEMBER_ME_FILTER" | "ANONYMOUS_FILTER" | "OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER" | "WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER" | "SESSION_MANAGEMENT_FILTER" | "EXCEPTION_TRANSLATION_FILTER" | "FILTER_SECURITY_INTERCEPTOR" | "SWITCH_USER_FILTER" | "LAST"
|
||||
named-security-filter = "FIRST" | "DISABLE_ENCODE_URL_FILTER" | "CHANNEL_FILTER" | "SECURITY_CONTEXT_FILTER" | "CONCURRENT_SESSION_FILTER" | "WEB_ASYNC_MANAGER_FILTER" | "HEADERS_FILTER" | "CORS_FILTER" | "SAML2_LOGOUT_REQUEST_FILTER" | "SAML2_LOGOUT_RESPONSE_FILTER" | "CSRF_FILTER" | "SAML2_LOGOUT_FILTER" | "LOGOUT_FILTER" | "OAUTH2_AUTHORIZATION_REQUEST_FILTER" | "SAML2_AUTHENTICATION_REQUEST_FILTER" | "X509_FILTER" | "PRE_AUTH_FILTER" | "CAS_FILTER" | "OAUTH2_LOGIN_FILTER" | "SAML2_AUTHENTICATION_FILTER" | "FORM_LOGIN_FILTER" | "OPENID_FILTER" | "LOGIN_PAGE_FILTER" |"LOGOUT_PAGE_FILTER" | "DIGEST_AUTH_FILTER" | "BEARER_TOKEN_AUTH_FILTER" | "BASIC_AUTH_FILTER" | "REQUEST_CACHE_FILTER" | "SERVLET_API_SUPPORT_FILTER" | "JAAS_API_SUPPORT_FILTER" | "REMEMBER_ME_FILTER" | "ANONYMOUS_FILTER" | "OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER" | "WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER" | "SESSION_MANAGEMENT_FILTER" | "EXCEPTION_TRANSLATION_FILTER" | "FILTER_SECURITY_INTERCEPTOR" | "SWITCH_USER_FILTER" | "LAST"
|
||||
|
@ -3720,6 +3720,7 @@
|
||||
<xs:simpleType name="named-security-filter">
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:enumeration value="FIRST"/>
|
||||
<xs:enumeration value="DISABLE_ENCODE_URL_FILTER"/>
|
||||
<xs:enumeration value="CHANNEL_FILTER"/>
|
||||
<xs:enumeration value="SECURITY_CONTEXT_FILTER"/>
|
||||
<xs:enumeration value="CONCURRENT_SESSION_FILTER"/>
|
||||
|
@ -53,7 +53,7 @@ public class FilterOrderRegistrationTests {
|
||||
|
||||
@Test
|
||||
public void putWhenPredefinedFilterThenDoesNotOverride() {
|
||||
int position = 100;
|
||||
int position = 200;
|
||||
Integer predefinedFilterOrderBefore = this.filterOrderRegistration.getOrder(ChannelProcessingFilter.class);
|
||||
this.filterOrderRegistration.put(MyFilter.class, position);
|
||||
Integer myFilterOrder = this.filterOrderRegistration.getOrder(MyFilter.class);
|
||||
|
@ -38,6 +38,7 @@ import org.springframework.security.config.test.SpringTestContextExtension;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.session.SessionRegistry;
|
||||
import org.springframework.security.core.userdetails.PasswordEncodedUser;
|
||||
import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy;
|
||||
import org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy;
|
||||
import org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy;
|
||||
@ -51,19 +52,26 @@ import org.springframework.security.web.session.SessionManagementFilter;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.springframework.security.config.Customizer.withDefaults;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
|
||||
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@ -295,6 +303,46 @@ public class SessionManagementConfigurerTests {
|
||||
verifyNoInteractions(SessionRegistryTwoBeansConfig.SESSION_REGISTRY_TWO);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenEnableSessionUrlRewritingTrueThenEncodeNotInvoked() throws Exception {
|
||||
this.spring.register(EnableUrlRewriteConfig.class).autowire();
|
||||
// @formatter:off
|
||||
this.mvc = MockMvcBuilders.webAppContextSetup(this.spring.getContext())
|
||||
.addFilters((request, response, chain) -> {
|
||||
HttpServletResponse responseToSpy = spy((HttpServletResponse) response);
|
||||
chain.doFilter(request, responseToSpy);
|
||||
verify(responseToSpy, atLeastOnce()).encodeRedirectURL(any());
|
||||
verify(responseToSpy, atLeastOnce()).encodeRedirectUrl(any());
|
||||
verify(responseToSpy, atLeastOnce()).encodeURL(any());
|
||||
verify(responseToSpy, atLeastOnce()).encodeUrl(any());
|
||||
})
|
||||
.apply(springSecurity())
|
||||
.build();
|
||||
// @formatter:on
|
||||
|
||||
this.mvc.perform(get("/")).andExpect(content().string("encoded"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenDefaultThenEncodeNotInvoked() throws Exception {
|
||||
this.spring.register(DefaultUrlRewriteConfig.class).autowire();
|
||||
// @formatter:off
|
||||
this.mvc = MockMvcBuilders.webAppContextSetup(this.spring.getContext())
|
||||
.addFilters((request, response, chain) -> {
|
||||
HttpServletResponse responseToSpy = spy((HttpServletResponse) response);
|
||||
chain.doFilter(request, responseToSpy);
|
||||
verify(responseToSpy, never()).encodeRedirectURL(any());
|
||||
verify(responseToSpy, never()).encodeRedirectUrl(any());
|
||||
verify(responseToSpy, never()).encodeURL(any());
|
||||
verify(responseToSpy, never()).encodeUrl(any());
|
||||
})
|
||||
.apply(springSecurity())
|
||||
.build();
|
||||
// @formatter:on
|
||||
|
||||
this.mvc.perform(get("/")).andExpect(content().string("encoded"));
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class SessionManagementRequestCacheConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@ -569,4 +617,49 @@ public class SessionManagementConfigurerTests {
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class DefaultUrlRewriteConfig {
|
||||
|
||||
@Bean
|
||||
DefaultSecurityFilterChain configure(HttpSecurity http) throws Exception {
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
EncodesUrls encodesUrls() {
|
||||
return new EncodesUrls();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class EnableUrlRewriteConfig {
|
||||
|
||||
@Bean
|
||||
DefaultSecurityFilterChain configure(HttpSecurity http) throws Exception {
|
||||
http.sessionManagement((sessions) -> sessions.enableSessionUrlRewriting(true));
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
EncodesUrls encodesUrls() {
|
||||
return new EncodesUrls();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@RestController
|
||||
static class EncodesUrls {
|
||||
|
||||
@RequestMapping("/")
|
||||
String encoded(HttpServletResponse response) {
|
||||
response.encodeURL("/foo");
|
||||
response.encodeUrl("/foo");
|
||||
response.encodeRedirectURL("/foo");
|
||||
response.encodeRedirectUrl("/foo");
|
||||
return "encoded";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -103,6 +103,7 @@ import org.springframework.security.web.header.HeaderWriterFilter;
|
||||
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.session.SessionManagementFilter;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
@ -121,6 +122,8 @@ import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.willAnswer;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
||||
@ -540,6 +543,28 @@ public class MiscHttpConfigTests {
|
||||
assertThat(response.getRedirectedUrl()).isEqualTo("http://localhost/login");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenUsingDisableUrlRewritingAndCustomRepositoryThenRedirectIsNotEncodedByResponse()
|
||||
throws IOException, ServletException {
|
||||
this.spring.configLocations(xml("DisableUrlRewriting-NullSecurityContextRepository")).autowire();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
|
||||
MockHttpServletResponse responseToSpy = spy(new MockHttpServletResponse());
|
||||
FilterChainProxy proxy = this.spring.getContext().getBean(FilterChainProxy.class);
|
||||
proxy.doFilter(request, responseToSpy, (req, resp) -> {
|
||||
HttpServletResponse httpResponse = (HttpServletResponse) resp;
|
||||
httpResponse.encodeUrl("/");
|
||||
httpResponse.encodeURL("/");
|
||||
httpResponse.encodeRedirectUrl("/");
|
||||
httpResponse.encodeRedirectURL("/");
|
||||
httpResponse.getWriter().write("encodeRedirect");
|
||||
});
|
||||
verify(responseToSpy, never()).encodeRedirectURL(any());
|
||||
verify(responseToSpy, never()).encodeRedirectUrl(any());
|
||||
verify(responseToSpy, never()).encodeURL(any());
|
||||
verify(responseToSpy, never()).encodeUrl(any());
|
||||
assertThat(responseToSpy.getContentAsString()).isEqualTo("encodeRedirect");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenUserDetailsServiceInParentContextThenLocatesSuccessfully() {
|
||||
assertThatExceptionOfType(BeansException.class).isThrownBy(
|
||||
@ -755,6 +780,7 @@ public class MiscHttpConfigTests {
|
||||
|
||||
private void assertThatFiltersMatchExpectedAutoConfigList(String url) {
|
||||
Iterator<Filter> filters = getFilters(url).iterator();
|
||||
assertThat(filters.next()).isInstanceOf(DisableEncodeUrlFilter.class);
|
||||
assertThat(filters.next()).isInstanceOf(SecurityContextPersistenceFilter.class);
|
||||
assertThat(filters.next()).isInstanceOf(WebAsyncManagerIntegrationFilter.class);
|
||||
assertThat(filters.next()).isInstanceOf(HeaderWriterFilter.class);
|
||||
|
@ -0,0 +1,32 @@
|
||||
<?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: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 auto-config="true" disable-url-rewriting="true" security-context-repository-ref="securityContextRepository">
|
||||
<intercept-url pattern="/**" access="permitAll"/>
|
||||
</http>
|
||||
<b:bean id="securityContextRepository" class="org.springframework.security.web.context.NullSecurityContextRepository"/>
|
||||
<b:import resource="userservice.xml"/>
|
||||
</b:beans>
|
@ -253,6 +253,10 @@ The filters are listed in the order in which they occur in the filter chain.
|
||||
|===
|
||||
| Alias | Filter Class | Namespace Element or Attribute
|
||||
|
||||
| DISABLE_ENCODE_URL_FILTER
|
||||
| `DisableEncodeUrlFilter`
|
||||
| `http@disable-url-rewriting`
|
||||
|
||||
| CHANNEL_FILTER
|
||||
| `ChannelProcessingFilter`
|
||||
| `http/intercept-url@requires-channel`
|
||||
|
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.session;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponseWrapper;
|
||||
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
/**
|
||||
* Disables encoding URLs using the {@link HttpServletResponse} to prevent including the
|
||||
* session id in URLs which is not considered URL because the session id can be leaked in
|
||||
* things like HTTP access logs.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 5.7
|
||||
*/
|
||||
public class DisableEncodeUrlFilter extends OncePerRequestFilter {
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
filterChain.doFilter(request, new DisableEncodeUrlResponseWrapper(response));
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables URL rewriting for the {@link HttpServletResponse} to prevent including the
|
||||
* session id in URLs which is not considered URL because the session id can be leaked
|
||||
* in things like HTTP access logs.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 5.7
|
||||
*/
|
||||
private static final class DisableEncodeUrlResponseWrapper extends HttpServletResponseWrapper {
|
||||
|
||||
/**
|
||||
* Constructs a response adaptor wrapping the given response.
|
||||
* @param response the {@link HttpServletResponse} to be wrapped.
|
||||
* @throws IllegalArgumentException if the response is null
|
||||
*/
|
||||
private DisableEncodeUrlResponseWrapper(HttpServletResponse response) {
|
||||
super(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encodeRedirectUrl(String url) {
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encodeRedirectURL(String url) {
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encodeUrl(String url) {
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encodeURL(String url) {
|
||||
return url;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.session;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class DisableEncodeUrlFilterTests {
|
||||
|
||||
@Mock
|
||||
private HttpServletRequest request;
|
||||
|
||||
@Mock
|
||||
private HttpServletResponse response;
|
||||
|
||||
private DisableEncodeUrlFilter filter = new DisableEncodeUrlFilter();
|
||||
|
||||
@Test
|
||||
void doFilterDisablesEncodeURL() throws Exception {
|
||||
verifyDoFilterDoesNotInteractWithResponse((httpResponse) -> httpResponse.encodeURL("/"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void doFilterDisablesEncodeUrl() throws Exception {
|
||||
verifyDoFilterDoesNotInteractWithResponse((httpResponse) -> httpResponse.encodeUrl("/"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void doFilterDisablesEncodeRedirectURL() throws Exception {
|
||||
verifyDoFilterDoesNotInteractWithResponse((httpResponse) -> httpResponse.encodeRedirectURL("/"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void doFilterDisablesEncodeRedirectUrl() throws Exception {
|
||||
verifyDoFilterDoesNotInteractWithResponse((httpResponse) -> httpResponse.encodeRedirectUrl("/"));
|
||||
}
|
||||
|
||||
private void verifyDoFilterDoesNotInteractWithResponse(Consumer<HttpServletResponse> toInvoke) throws Exception {
|
||||
this.filter.doFilter(this.request, this.response, (request, response) -> {
|
||||
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||||
toInvoke.accept(httpResponse);
|
||||
});
|
||||
verifyNoInteractions(this.response);
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user