Skip to content

Commit 5ef0ee0

Browse files
committed
Prevent early initialization of SessionRepository beans
Replace `SessionRepositoryFilterConfiguration` filter registration bean with a `DelegatingFilterProxyRegistrationBean` so that `SessionRepository` beans are not initialized early. Fixes gh-35240
1 parent 7c2c2eb commit 5ef0ee0

File tree

5 files changed

+106
-27
lines changed

5 files changed

+106
-27
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/SessionRepositoryFilterConfiguration.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@
2121

2222
import javax.servlet.DispatcherType;
2323

24+
import org.springframework.beans.factory.ListableBeanFactory;
2425
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
2526
import org.springframework.boot.context.properties.EnableConfigurationProperties;
26-
import org.springframework.boot.web.servlet.FilterRegistrationBean;
27+
import org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBean;
2728
import org.springframework.context.annotation.Bean;
2829
import org.springframework.context.annotation.Configuration;
2930
import org.springframework.session.web.http.SessionRepositoryFilter;
31+
import org.springframework.util.Assert;
3032

3133
/**
3234
* Configuration for customizing the registration of the {@link SessionRepositoryFilter}.
@@ -39,9 +41,12 @@
3941
class SessionRepositoryFilterConfiguration {
4042

4143
@Bean
42-
FilterRegistrationBean<SessionRepositoryFilter<?>> sessionRepositoryFilterRegistration(
43-
SessionProperties sessionProperties, SessionRepositoryFilter<?> filter) {
44-
FilterRegistrationBean<SessionRepositoryFilter<?>> registration = new FilterRegistrationBean<>(filter);
44+
DelegatingFilterProxyRegistrationBean sessionRepositoryFilterRegistration(SessionProperties sessionProperties,
45+
ListableBeanFactory beanFactory) {
46+
String[] targetBeanNames = beanFactory.getBeanNamesForType(SessionRepositoryFilter.class, false, false);
47+
Assert.state(targetBeanNames.length == 1, "Expected single SessionRepositoryFilter bean");
48+
DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
49+
targetBeanNames[0]);
4550
registration.setDispatcherTypes(getDispatcherTypes(sessionProperties));
4651
registration.setOrder(sessionProperties.getServlet().getFilterOrder());
4752
return registration;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright 2012-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.session;
18+
19+
import java.util.LinkedHashMap;
20+
21+
import org.junit.jupiter.api.Test;
22+
23+
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
24+
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
25+
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
26+
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
27+
import org.springframework.context.ConfigurableApplicationContext;
28+
import org.springframework.context.annotation.Bean;
29+
import org.springframework.context.annotation.Configuration;
30+
import org.springframework.session.MapSessionRepository;
31+
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
32+
import org.springframework.util.Assert;
33+
34+
import static org.assertj.core.api.Assertions.assertThat;
35+
36+
/**
37+
* Integration tests to ensure {@link SessionAutoConfiguration} and
38+
* {@link SessionRepositoryFilterConfiguration} does not cause early initialization.
39+
*
40+
* @author Phillip Webb
41+
*/
42+
public class SessionAutoConfigurationEarlyInitializationIntegrationTests {
43+
44+
@Test
45+
void configurationIsFrozenWhenSessionRepositoryAccessed() {
46+
new WebApplicationContextRunner(AnnotationConfigServletWebServerApplicationContext::new)
47+
.withSystemProperties("spring.jndi.ignore=true")
48+
.withPropertyValues("server.port=0")
49+
.withUserConfiguration(TestConfiguration.class)
50+
.run((context) -> assertThat(context).hasSingleBean(MapSessionRepository.class));
51+
}
52+
53+
@Configuration(proxyBeanMethods = false)
54+
@EnableSpringHttpSession
55+
@ImportAutoConfiguration({ ServletWebServerFactoryAutoConfiguration.class, SessionAutoConfiguration.class })
56+
static class TestConfiguration {
57+
58+
@Bean
59+
MapSessionRepository mapSessionRepository(ConfigurableApplicationContext context) {
60+
Assert.isTrue(context.getBeanFactory().isConfigurationFrozen(), "Context should be frozen");
61+
return new MapSessionRepository(new LinkedHashMap<>());
62+
}
63+
64+
}
65+
66+
}

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationJdbcTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
import org.springframework.boot.test.context.FilteredClassLoader;
3737
import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext;
3838
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
39-
import org.springframework.boot.web.servlet.FilterRegistrationBean;
39+
import org.springframework.boot.web.servlet.AbstractFilterRegistrationBean;
4040
import org.springframework.context.annotation.Bean;
4141
import org.springframework.context.annotation.Configuration;
4242
import org.springframework.context.annotation.Primary;
@@ -100,7 +100,7 @@ void filterOrderCanBeCustomized() {
100100
this.contextRunner
101101
.withPropertyValues("spring.session.store-type=jdbc", "spring.session.servlet.filter-order=123")
102102
.run((context) -> {
103-
FilterRegistrationBean<?> registration = context.getBean(FilterRegistrationBean.class);
103+
AbstractFilterRegistrationBean<?> registration = context.getBean(AbstractFilterRegistrationBean.class);
104104
assertThat(registration.getOrder()).isEqualTo(123);
105105
});
106106
}

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationTests.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
import org.springframework.boot.autoconfigure.web.ServerProperties;
2929
import org.springframework.boot.context.properties.EnableConfigurationProperties;
3030
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
31-
import org.springframework.boot.web.servlet.FilterRegistrationBean;
31+
import org.springframework.boot.web.servlet.AbstractFilterRegistrationBean;
3232
import org.springframework.context.annotation.Bean;
3333
import org.springframework.context.annotation.Configuration;
3434
import org.springframework.core.annotation.Order;
@@ -41,6 +41,7 @@
4141
import org.springframework.session.web.http.HeaderHttpSessionIdResolver;
4242
import org.springframework.session.web.http.HttpSessionIdResolver;
4343
import org.springframework.session.web.http.SessionRepositoryFilter;
44+
import org.springframework.web.filter.DelegatingFilterProxy;
4445

4546
import static org.assertj.core.api.Assertions.assertThat;
4647
import static org.mockito.ArgumentMatchers.any;
@@ -99,8 +100,16 @@ void backOffIfSessionRepositoryIsPresent() {
99100
@Test
100101
void filterIsRegisteredWithAsyncErrorAndRequestDispatcherTypes() {
101102
this.contextRunner.withUserConfiguration(SessionRepositoryConfiguration.class).run((context) -> {
102-
FilterRegistrationBean<?> registration = context.getBean(FilterRegistrationBean.class);
103-
assertThat(registration.getFilter()).isSameAs(context.getBean(SessionRepositoryFilter.class));
103+
AbstractFilterRegistrationBean<?> registration = context.getBean(AbstractFilterRegistrationBean.class);
104+
DelegatingFilterProxy delegatingFilterProxy = (DelegatingFilterProxy) registration.getFilter();
105+
try {
106+
// Trigger actual initialization
107+
delegatingFilterProxy.doFilter(null, null, null);
108+
}
109+
catch (Exception ex) {
110+
}
111+
assertThat(delegatingFilterProxy).extracting("delegate")
112+
.isSameAs(context.getBean(SessionRepositoryFilter.class));
104113
assertThat(registration)
105114
.extracting("dispatcherTypes", InstanceOfAssertFactories.iterable(DispatcherType.class))
106115
.containsOnly(DispatcherType.ASYNC, DispatcherType.ERROR, DispatcherType.REQUEST);
@@ -112,7 +121,7 @@ void filterOrderCanBeCustomizedWithCustomStore() {
112121
this.contextRunner.withUserConfiguration(SessionRepositoryConfiguration.class)
113122
.withPropertyValues("spring.session.servlet.filter-order=123")
114123
.run((context) -> {
115-
FilterRegistrationBean<?> registration = context.getBean(FilterRegistrationBean.class);
124+
AbstractFilterRegistrationBean<?> registration = context.getBean(AbstractFilterRegistrationBean.class);
116125
assertThat(registration.getOrder()).isEqualTo(123);
117126
});
118127
}
@@ -122,7 +131,7 @@ void filterDispatcherTypesCanBeCustomized() {
122131
this.contextRunner.withUserConfiguration(SessionRepositoryConfiguration.class)
123132
.withPropertyValues("spring.session.servlet.filter-dispatcher-types=error, request")
124133
.run((context) -> {
125-
FilterRegistrationBean<?> registration = context.getBean(FilterRegistrationBean.class);
134+
AbstractFilterRegistrationBean<?> registration = context.getBean(AbstractFilterRegistrationBean.class);
126135
assertThat(registration)
127136
.extracting("dispatcherTypes", InstanceOfAssertFactories.iterable(DispatcherType.class))
128137
.containsOnly(DispatcherType.ERROR, DispatcherType.REQUEST);
@@ -134,7 +143,7 @@ void emptyFilterDispatcherTypesDoNotThrowException() {
134143
this.contextRunner.withUserConfiguration(SessionRepositoryConfiguration.class)
135144
.withPropertyValues("spring.session.servlet.filter-dispatcher-types=")
136145
.run((context) -> {
137-
FilterRegistrationBean<?> registration = context.getBean(FilterRegistrationBean.class);
146+
AbstractFilterRegistrationBean<?> registration = context.getBean(AbstractFilterRegistrationBean.class);
138147
assertThat(registration)
139148
.extracting("dispatcherTypes", InstanceOfAssertFactories.iterable(DispatcherType.class))
140149
.isEmpty();

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/FilterOrderingIntegrationTests.java

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
package org.springframework.boot.autoconfigure.web.servlet;
1818

19-
import java.util.ArrayList;
20-
import java.util.Iterator;
2119
import java.util.List;
2220
import java.util.concurrent.ConcurrentHashMap;
2321

@@ -44,7 +42,7 @@
4442
import org.springframework.security.web.FilterChainProxy;
4543
import org.springframework.session.MapSessionRepository;
4644
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
47-
import org.springframework.session.web.http.SessionRepositoryFilter;
45+
import org.springframework.web.filter.DelegatingFilterProxy;
4846

4947
import static org.assertj.core.api.Assertions.assertThat;
5048
import static org.mockito.BDDMockito.given;
@@ -73,18 +71,19 @@ void testFilterOrdering() {
7371
List<RegisteredFilter> registeredFilters = this.context.getBean(MockServletWebServerFactory.class)
7472
.getWebServer()
7573
.getRegisteredFilters();
76-
List<Filter> filters = new ArrayList<>(registeredFilters.size());
77-
for (RegisteredFilter registeredFilter : registeredFilters) {
78-
filters.add(registeredFilter.getFilter());
79-
}
80-
Iterator<Filter> iterator = filters.iterator();
81-
assertThat(iterator.next()).isInstanceOf(OrderedCharacterEncodingFilter.class);
82-
assertThat(iterator.next()).isInstanceOf(SessionRepositoryFilter.class);
83-
assertThat(iterator.next()).isInstanceOf(Filter.class);
84-
assertThat(iterator.next()).isInstanceOf(Filter.class);
85-
assertThat(iterator.next()).isInstanceOf(OrderedRequestContextFilter.class);
86-
assertThat(iterator.next()).isInstanceOf(ErrorPageSecurityFilter.class);
87-
assertThat(iterator.next()).isInstanceOf(FilterChainProxy.class);
74+
assertThat(registeredFilters.get(0).getFilter()).isInstanceOf(OrderedCharacterEncodingFilter.class);
75+
assertThat(registeredFilters.get(1).getFilter()).isInstanceOf(DelegatingFilterProxy.class)
76+
.extracting("targetBeanName")
77+
.isEqualTo("springSessionRepositoryFilter");
78+
assertThat(registeredFilters.get(2).getFilter()).isInstanceOf(Filter.class)
79+
.extracting("beanName")
80+
.isEqualTo("hiddenHttpMethodFilter");
81+
assertThat(registeredFilters.get(3).getFilter()).isInstanceOf(Filter.class)
82+
.extracting("beanName")
83+
.isEqualTo("formContentFilter");
84+
assertThat(registeredFilters.get(4).getFilter()).isInstanceOf(OrderedRequestContextFilter.class);
85+
assertThat(registeredFilters.get(5).getFilter()).isInstanceOf(ErrorPageSecurityFilter.class);
86+
assertThat(registeredFilters.get(6).getFilter()).isInstanceOf(FilterChainProxy.class);
8887
}
8988

9089
private void load() {

0 commit comments

Comments
 (0)