Skip to content

Commit 18a9a22

Browse files
committed
Merge pull request #20961 from vpavic
* gh-20961: Polish "Add support for customizing Spring Session's cookie serializer" Add support for customizing Spring Session's cookie serializer Closes gh-20961
2 parents f3d717e + d65d951 commit 18a9a22

File tree

3 files changed

+89
-19
lines changed

3 files changed

+89
-19
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2012-2020 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 org.springframework.session.web.http.DefaultCookieSerializer;
20+
21+
/**
22+
* Callback interface that can be implemented by beans wishing to customize the
23+
* {@link DefaultCookieSerializer} configuration.
24+
*
25+
* @author Vedran Pavic
26+
* @since 2.3.0
27+
*/
28+
@FunctionalInterface
29+
public interface CookieSerializerCustomizer {
30+
31+
/**
32+
* Customize the cookie serializer.
33+
* @param cookieSerializer the {@code CookieSerializer} to customize
34+
*/
35+
void customize(DefaultCookieSerializer cookieSerializer);
36+
37+
}

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

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import org.springframework.context.annotation.Import;
5454
import org.springframework.context.annotation.ImportSelector;
5555
import org.springframework.core.type.AnnotationMetadata;
56+
import org.springframework.security.web.authentication.RememberMeServices;
5657
import org.springframework.session.ReactiveSessionRepository;
5758
import org.springframework.session.Session;
5859
import org.springframework.session.SessionRepository;
@@ -61,7 +62,6 @@
6162
import org.springframework.session.web.http.CookieSerializer;
6263
import org.springframework.session.web.http.DefaultCookieSerializer;
6364
import org.springframework.session.web.http.HttpSessionIdResolver;
64-
import org.springframework.util.ClassUtils;
6565

6666
/**
6767
* {@link EnableAutoConfiguration Auto-configuration} for Spring Session.
@@ -83,16 +83,15 @@
8383
@AutoConfigureBefore(HttpHandlerAutoConfiguration.class)
8484
public class SessionAutoConfiguration {
8585

86-
private static final String REMEMBER_ME_SERVICES_CLASS = "org.springframework.security.web.authentication.RememberMeServices";
87-
8886
@Configuration(proxyBeanMethods = false)
8987
@ConditionalOnWebApplication(type = Type.SERVLET)
9088
@Import({ ServletSessionRepositoryValidator.class, SessionRepositoryFilterConfiguration.class })
9189
static class ServletSessionConfiguration {
9290

9391
@Bean
9492
@Conditional(DefaultCookieSerializerCondition.class)
95-
DefaultCookieSerializer cookieSerializer(ServerProperties serverProperties) {
93+
DefaultCookieSerializer cookieSerializer(ServerProperties serverProperties,
94+
ObjectProvider<CookieSerializerCustomizer> cookieSerializerCustomizers) {
9695
Cookie cookie = serverProperties.getServlet().getSession().getCookie();
9796
DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
9897
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
@@ -102,12 +101,22 @@ DefaultCookieSerializer cookieSerializer(ServerProperties serverProperties) {
102101
map.from(cookie::getHttpOnly).to(cookieSerializer::setUseHttpOnlyCookie);
103102
map.from(cookie::getSecure).to(cookieSerializer::setUseSecureCookie);
104103
map.from(cookie::getMaxAge).to((maxAge) -> cookieSerializer.setCookieMaxAge((int) maxAge.getSeconds()));
105-
if (ClassUtils.isPresent(REMEMBER_ME_SERVICES_CLASS, getClass().getClassLoader())) {
106-
new RememberMeServicesCookieSerializerCustomizer().apply(cookieSerializer);
107-
}
104+
cookieSerializerCustomizers.orderedStream().forEach((customizer) -> customizer.customize(cookieSerializer));
108105
return cookieSerializer;
109106
}
110107

108+
@Configuration(proxyBeanMethods = false)
109+
@ConditionalOnClass(RememberMeServices.class)
110+
static class RememberMeServicesConfiguration {
111+
112+
@Bean
113+
CookieSerializerCustomizer rememberMeServicesCookieSerializerCustomizer() {
114+
return (cookieSerializer) -> cookieSerializer
115+
.setRememberMeRequestAttribute(SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR);
116+
}
117+
118+
}
119+
111120
@Configuration(proxyBeanMethods = false)
112121
@ConditionalOnMissingBean(SessionRepository.class)
113122
@Import({ ServletSessionRepositoryImplementationValidator.class,
@@ -133,18 +142,6 @@ static class ReactiveSessionRepositoryConfiguration {
133142

134143
}
135144

136-
/**
137-
* Customization for {@link SpringSessionRememberMeServices} that is only instantiated
138-
* when Spring Security is on the classpath.
139-
*/
140-
static class RememberMeServicesCookieSerializerCustomizer {
141-
142-
void apply(DefaultCookieSerializer cookieSerializer) {
143-
cookieSerializer.setRememberMeRequestAttribute(SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR);
144-
}
145-
146-
}
147-
148145
/**
149146
* Condition to trigger the creation of a {@link DefaultCookieSerializer}. This kicks
150147
* in if either no {@link HttpSessionIdResolver} and {@link CookieSerializer} beans

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import javax.servlet.DispatcherType;
2323

2424
import org.junit.jupiter.api.Test;
25+
import org.mockito.InOrder;
2526

2627
import org.springframework.boot.autoconfigure.AutoConfigurations;
2728
import org.springframework.boot.autoconfigure.web.ServerProperties;
@@ -30,6 +31,7 @@
3031
import org.springframework.boot.web.servlet.FilterRegistrationBean;
3132
import org.springframework.context.annotation.Bean;
3233
import org.springframework.context.annotation.Configuration;
34+
import org.springframework.core.annotation.Order;
3335
import org.springframework.session.MapSessionRepository;
3436
import org.springframework.session.SessionRepository;
3537
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
@@ -42,6 +44,8 @@
4244
import org.springframework.test.util.ReflectionTestUtils;
4345

4446
import static org.assertj.core.api.Assertions.assertThat;
47+
import static org.mockito.ArgumentMatchers.any;
48+
import static org.mockito.Mockito.inOrder;
4549
import static org.mockito.Mockito.mock;
4650

4751
/**
@@ -205,6 +209,16 @@ void autoConfiguredCookieSerializerIsConfiguredWithRememberMeRequestAttribute()
205209
});
206210
}
207211

212+
@Test
213+
void cookieSerializerCustomization() {
214+
this.contextRunner.withBean(CookieSerializerCustomization.class).run((context) -> {
215+
CookieSerializerCustomization customization = context.getBean(CookieSerializerCustomization.class);
216+
InOrder inOrder = inOrder(customization.customizer1, customization.customizer2);
217+
inOrder.verify(customization.customizer1).customize(any());
218+
inOrder.verify(customization.customizer2).customize(any());
219+
});
220+
}
221+
208222
@Configuration(proxyBeanMethods = false)
209223
@EnableSpringHttpSession
210224
static class SessionRepositoryConfiguration {
@@ -276,4 +290,26 @@ SpringSessionRememberMeServices rememberMeServices() {
276290

277291
}
278292

293+
@Configuration(proxyBeanMethods = false)
294+
@EnableSpringHttpSession
295+
static class CookieSerializerCustomization extends SessionRepositoryConfiguration {
296+
297+
private final CookieSerializerCustomizer customizer1 = mock(CookieSerializerCustomizer.class);
298+
299+
private final CookieSerializerCustomizer customizer2 = mock(CookieSerializerCustomizer.class);
300+
301+
@Bean
302+
@Order(1)
303+
CookieSerializerCustomizer customizer1() {
304+
return this.customizer1;
305+
}
306+
307+
@Bean
308+
@Order(2)
309+
CookieSerializerCustomizer customizer2() {
310+
return this.customizer2;
311+
}
312+
313+
}
314+
279315
}

0 commit comments

Comments
 (0)