Skip to content

Commit 51f55fd

Browse files
committed
Add support for unsetting the Session Cookie SameSite
1 parent d005e95 commit 51f55fd

File tree

9 files changed

+53
-7
lines changed

9 files changed

+53
-7
lines changed

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,9 @@ DefaultCookieSerializer cookieSerializer(ServerProperties serverProperties,
9797
map.from(cookie::getHttpOnly).to(cookieSerializer::setUseHttpOnlyCookie);
9898
map.from(cookie::getSecure).to(cookieSerializer::setUseSecureCookie);
9999
map.from(cookie::getMaxAge).asInt(Duration::getSeconds).to(cookieSerializer::setCookieMaxAge);
100-
map.from(cookie::getSameSite).as(SameSite::attributeValue).to(cookieSerializer::setSameSite);
100+
map.from(cookie::getSameSite)
101+
.as((sameSite) -> (sameSite == SameSite.UNSET) ? null : sameSite.attributeValue())
102+
.to(cookieSerializer::setSameSite);
101103
map.from(cookie::getPartitioned).to(cookieSerializer::setPartitioned);
102104
cookieSerializerCustomizers.orderedStream().forEach((customizer) -> customizer.customize(cookieSerializer));
103105
return cookieSerializer;

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebSessionIdResolverAutoConfiguration.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ private void initializeCookie(ResponseCookieBuilder builder) {
8282

8383
private String getSameSite(Cookie properties) {
8484
SameSite sameSite = properties.getSameSite();
85-
return (sameSite != null) ? sameSite.attributeValue() : null;
85+
return (sameSite != null && sameSite != SameSite.UNSET) ? sameSite.attributeValue() : null;
8686
}
8787

8888
}

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

+10
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,16 @@ void sessionCookieConfigurationIsAppliedToAutoConfiguredCookieSerializer() {
170170
});
171171
}
172172

173+
@Test
174+
void sessionCookieSameSiteUnsetIsAppliedToAutoConfiguredCookieSerializer() {
175+
this.contextRunner.withUserConfiguration(SessionRepositoryConfiguration.class)
176+
.withPropertyValues("server.servlet.session.cookie.sameSite=unset")
177+
.run((context) -> {
178+
DefaultCookieSerializer cookieSerializer = context.getBean(DefaultCookieSerializer.class);
179+
assertThat(cookieSerializer).hasFieldOrPropertyWithValue("sameSite", null);
180+
});
181+
}
182+
173183
@Test
174184
void autoConfiguredCookieSerializerIsUsedBySessionRepositoryFilter() {
175185
this.contextRunner.withUserConfiguration(SessionRepositoryConfiguration.class)

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfigurationTests.java

+9
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,15 @@ void customSessionCookieConfigurationShouldBeApplied() {
676676
}));
677677
}
678678

679+
@Test
680+
void sessionCookieUnsetConfigurationShouldBeApplied() {
681+
this.contextRunner.withPropertyValues("server.reactive.session.cookie.same-site:unset")
682+
.run(assertExchangeWithSession((exchange) -> {
683+
List<ResponseCookie> cookies = exchange.getResponse().getCookies().get("SESSION");
684+
assertThat(cookies).extracting(ResponseCookie::getSameSite).containsOnlyNulls();
685+
}));
686+
}
687+
679688
@ParameterizedTest
680689
@ValueSource(classes = { ServerProperties.class, WebFluxProperties.class })
681690
void propertiesAreNotEnabledInNonWebApplication(Class<?> propertiesClass) {

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactory.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ protected final void configureWebAppContext(WebAppContext context, ServletContex
284284
private void configureSession(WebAppContext context) {
285285
SessionHandler handler = context.getSessionHandler();
286286
SameSite sessionSameSite = getSession().getCookie().getSameSite();
287-
if (sessionSameSite != null) {
287+
if (sessionSameSite != null && sessionSameSite != SameSite.UNSET) {
288288
handler.setSameSite(HttpCookie.SameSite.valueOf(sessionSameSite.name()));
289289
}
290290
Duration sessionTimeout = getSession().getTimeout();

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactory.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -998,7 +998,7 @@ private static class SuppliedSameSiteCookieProcessor extends Rfc6265CookieProces
998998
@Override
999999
public String generateHeader(Cookie cookie, HttpServletRequest request) {
10001000
SameSite sameSite = getSameSite(cookie);
1001-
if (sameSite == null) {
1001+
if (sameSite == null || sameSite == SameSite.UNSET) {
10021002
return super.generateHeader(cookie, request);
10031003
}
10041004
Rfc6265CookieProcessor delegate = new Rfc6265CookieProcessor();

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServerFactory.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,10 @@ public void handleRequest(HttpServerExchange exchange) throws Exception {
635635
private void beforeCommit(HttpServerExchange exchange) {
636636
for (Cookie cookie : exchange.responseCookies()) {
637637
SameSite sameSite = getSameSite(asServletCookie(cookie));
638-
if (sameSite != null) {
638+
if (sameSite == SameSite.UNSET) {
639+
cookie.setSameSite(false);
640+
}
641+
else if (sameSite != null) {
639642
cookie.setSameSiteMode(sameSite.attributeValue());
640643
}
641644
}

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/Cookie.java

+5
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,11 @@ public void setPartitioned(Boolean partitioned) {
145145
*/
146146
public enum SameSite {
147147

148+
/**
149+
* Don't set the SameSite cookie attribute.
150+
*/
151+
UNSET("Unset"),
152+
148153
/**
149154
* Cookies are sent in both first-party and cross-origin requests.
150155
*/

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java

+19-2
Original file line numberDiff line numberDiff line change
@@ -881,7 +881,7 @@ void sessionCookieConfiguration() {
881881
}
882882

883883
@ParameterizedTest
884-
@EnumSource
884+
@EnumSource(mode = EnumSource.Mode.EXCLUDE, names = "UNSET")
885885
void sessionCookieSameSiteAttributeCanBeConfiguredAndOnlyAffectsSessionCookies(SameSite sameSite) throws Exception {
886886
AbstractServletWebServerFactory factory = getFactory();
887887
factory.getSession().getCookie().setSameSite(sameSite);
@@ -896,7 +896,7 @@ void sessionCookieSameSiteAttributeCanBeConfiguredAndOnlyAffectsSessionCookies(S
896896
}
897897

898898
@ParameterizedTest
899-
@EnumSource
899+
@EnumSource(mode = EnumSource.Mode.EXCLUDE, names = "UNSET")
900900
void sessionCookieSameSiteAttributeCanBeConfiguredAndOnlyAffectsSessionCookiesWhenUsingCustomName(SameSite sameSite)
901901
throws Exception {
902902
AbstractServletWebServerFactory factory = getFactory();
@@ -949,6 +949,23 @@ void cookieSameSiteSuppliersShouldNotAffectSessionCookie() throws IOException, U
949949
(header) -> assertThat(header).contains("test=test").contains("SameSite=Strict"));
950950
}
951951

952+
@Test
953+
void cookieSameSiteSuppliersShouldNotAffectUnsetSameSite() throws IOException, URISyntaxException {
954+
AbstractServletWebServerFactory factory = getFactory();
955+
factory.getSession().getCookie().setSameSite(SameSite.UNSET);
956+
factory.getSession().getCookie().setName("SESSIONCOOKIE");
957+
factory.addCookieSameSiteSuppliers(CookieSameSiteSupplier.ofStrict());
958+
factory.addInitializers(new ServletRegistrationBean<>(new CookieServlet(false), "/"));
959+
this.webServer = factory.getWebServer();
960+
this.webServer.start();
961+
ClientHttpResponse clientResponse = getClientResponse(getLocalUrl("/"));
962+
assertThat(clientResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
963+
List<String> setCookieHeaders = clientResponse.getHeaders().get("Set-Cookie");
964+
assertThat(setCookieHeaders).satisfiesExactlyInAnyOrder(
965+
(header) -> assertThat(header).contains("SESSIONCOOKIE").doesNotContain("SameSite"),
966+
(header) -> assertThat(header).contains("test=test").contains("SameSite=Strict"));
967+
}
968+
952969
@Test
953970
protected void sslSessionTracking() {
954971
AbstractServletWebServerFactory factory = getFactory();

0 commit comments

Comments
 (0)