Skip to content

Commit 437c259

Browse files
committed
Merge pull request #44714 from filiphr
* gh-44714: Polish "Add support for omitting SameSite attribute from session cookie" Add support for omitting SameSite attribute from session cookie Closes gh-44714
2 parents 9920b5f + 6367074 commit 437c259

File tree

8 files changed

+61
-20
lines changed

8 files changed

+61
-20
lines changed

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

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2024 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -77,12 +77,7 @@ private void initializeCookie(ResponseCookieBuilder builder) {
7777
map.from(cookie::getSecure).to(builder::secure);
7878
map.from(cookie::getMaxAge).to(builder::maxAge);
7979
map.from(cookie::getPartitioned).to(builder::partitioned);
80-
map.from(getSameSite(cookie)).to(builder::sameSite);
81-
}
82-
83-
private String getSameSite(Cookie properties) {
84-
SameSite sameSite = properties.getSameSite();
85-
return (sameSite != null) ? sameSite.attributeValue() : null;
80+
map.from(cookie::getSameSite).as(SameSite::attributeValue).to(builder::sameSite);
8681
}
8782

8883
}

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2024 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -170,6 +170,16 @@ void sessionCookieConfigurationIsAppliedToAutoConfiguredCookieSerializer() {
170170
});
171171
}
172172

173+
@Test
174+
void sessionCookieSameSiteOmittedIsAppliedToAutoConfiguredCookieSerializer() {
175+
this.contextRunner.withUserConfiguration(SessionRepositoryConfiguration.class)
176+
.withPropertyValues("server.servlet.session.cookie.sameSite=omitted")
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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,15 @@ void customSessionCookieConfigurationShouldBeApplied() {
676676
}));
677677
}
678678

679+
@Test
680+
void sessionCookieOmittedConfigurationShouldBeApplied() {
681+
this.contextRunner.withPropertyValues("server.reactive.session.cookie.same-site:omitted")
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

Lines changed: 1 addition & 1 deletion
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.OMITTED) {
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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -998,11 +998,12 @@ 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+
String sameSiteValue = (sameSite != null) ? sameSite.attributeValue() : null;
1002+
if (sameSiteValue == null) {
10021003
return super.generateHeader(cookie, request);
10031004
}
10041005
Rfc6265CookieProcessor delegate = new Rfc6265CookieProcessor();
1005-
delegate.setSameSiteCookies(sameSite.attributeValue());
1006+
delegate.setSameSiteCookies(sameSiteValue);
10061007
return delegate.generateHeader(cookie, request);
10071008
}
10081009

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

Lines changed: 4 additions & 1 deletion
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.OMITTED) {
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

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2024 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -146,19 +146,25 @@ public void setPartitioned(Boolean partitioned) {
146146
public enum SameSite {
147147

148148
/**
149-
* Cookies are sent in both first-party and cross-origin requests.
149+
* SameSite attribute will be omitted when creating the cookie.
150+
*/
151+
OMITTED(null),
152+
153+
/**
154+
* SameSite attribute will be set to None. Cookies are sent in both first-party
155+
* and cross-origin requests.
150156
*/
151157
NONE("None"),
152158

153159
/**
154-
* Cookies are sent in a first-party context, also when following a link to the
155-
* origin site.
160+
* SameSite attribute will be set to Lax. Cookies are sent in a first-party
161+
* context, also when following a link to the origin site.
156162
*/
157163
LAX("Lax"),
158164

159165
/**
160-
* Cookies are only sent in a first-party context (i.e. not when following a link
161-
* to the origin site).
166+
* SameSite attribute will be set to Strict. Cookies are only sent in a
167+
* first-party context (i.e. not when following a link to the origin site).
162168
*/
163169
STRICT("Strict");
164170

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

Lines changed: 19 additions & 2 deletions
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 = "OMITTED")
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 = "OMITTED")
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 cookieSameSiteSuppliersShouldNotAffectOmittedSameSite() throws IOException, URISyntaxException {
954+
AbstractServletWebServerFactory factory = getFactory();
955+
factory.getSession().getCookie().setSameSite(SameSite.OMITTED);
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)