Skip to content

Commit fcd92f3

Browse files
committed
Add support for partitioned cookies
spring-projectsgh-42307
1 parent 4877e4d commit fcd92f3

File tree

7 files changed

+34
-5
lines changed

7 files changed

+34
-5
lines changed

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 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.
@@ -98,6 +98,7 @@ DefaultCookieSerializer cookieSerializer(ServerProperties serverProperties,
9898
map.from(cookie::getSecure).to(cookieSerializer::setUseSecureCookie);
9999
map.from(cookie::getMaxAge).asInt(Duration::getSeconds).to(cookieSerializer::setCookieMaxAge);
100100
map.from(cookie::getSameSite).as(SameSite::attributeValue).to(cookieSerializer::setSameSite);
101+
map.from(cookie::getPartitioned).to(cookieSerializer::setPartitioned);
101102
cookieSerializerCustomizers.orderedStream().forEach((customizer) -> customizer.customize(cookieSerializer));
102103
return cookieSerializer;
103104
}

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2022 the original author or authors.
2+
* Copyright 2012-2024 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.
@@ -76,6 +76,7 @@ private void initializeCookie(ResponseCookieBuilder builder) {
7676
map.from(cookie::getHttpOnly).to(builder::httpOnly);
7777
map.from(cookie::getSecure).to(builder::secure);
7878
map.from(cookie::getMaxAge).to(builder::maxAge);
79+
map.from(cookie::getPartitioned).to(builder::partitioned);
7980
map.from(getSameSite(cookie)).to(builder::sameSite);
8081
}
8182

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ void sessionCookieConfigurationIsAppliedToAutoConfiguredCookieSerializer() {
156156
.withPropertyValues("server.servlet.session.cookie.name=sid", "server.servlet.session.cookie.domain=spring",
157157
"server.servlet.session.cookie.path=/test", "server.servlet.session.cookie.httpOnly=false",
158158
"server.servlet.session.cookie.secure=false", "server.servlet.session.cookie.maxAge=10s",
159-
"server.servlet.session.cookie.sameSite=strict")
159+
"server.servlet.session.cookie.sameSite=strict", "server.servlet.session.cookie.partitioned=true")
160160
.run((context) -> {
161161
DefaultCookieSerializer cookieSerializer = context.getBean(DefaultCookieSerializer.class);
162162
assertThat(cookieSerializer).hasFieldOrPropertyWithValue("cookieName", "sid");
@@ -166,6 +166,7 @@ void sessionCookieConfigurationIsAppliedToAutoConfiguredCookieSerializer() {
166166
assertThat(cookieSerializer).hasFieldOrPropertyWithValue("useSecureCookie", false);
167167
assertThat(cookieSerializer).hasFieldOrPropertyWithValue("cookieMaxAge", 10);
168168
assertThat(cookieSerializer).hasFieldOrPropertyWithValue("sameSite", "Strict");
169+
assertThat(cookieSerializer).hasFieldOrPropertyWithValue("partitioned", true);
169170
});
170171
}
171172

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -644,7 +644,8 @@ void customSessionCookieConfigurationShouldBeApplied() {
644644
this.contextRunner.withPropertyValues("server.reactive.session.cookie.name:JSESSIONID",
645645
"server.reactive.session.cookie.domain:.example.com", "server.reactive.session.cookie.path:/example",
646646
"server.reactive.session.cookie.max-age:60", "server.reactive.session.cookie.http-only:false",
647-
"server.reactive.session.cookie.secure:false", "server.reactive.session.cookie.same-site:strict")
647+
"server.reactive.session.cookie.secure:false", "server.reactive.session.cookie.same-site:strict",
648+
"server.reactive.session.cookie.partitioned:true")
648649
.run(assertExchangeWithSession((exchange) -> {
649650
List<ResponseCookie> cookies = exchange.getResponse().getCookies().get("JSESSIONID");
650651
assertThat(cookies).isNotEmpty();
@@ -654,6 +655,7 @@ void customSessionCookieConfigurationShouldBeApplied() {
654655
assertThat(cookies).allMatch((cookie) -> !cookie.isHttpOnly());
655656
assertThat(cookies).allMatch((cookie) -> !cookie.isSecure());
656657
assertThat(cookies).allMatch((cookie) -> cookie.getSameSite().equals("Strict"));
658+
assertThat(cookies).allMatch(ResponseCookie::isPartitioned);
657659
}));
658660
}
659661

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

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2021 the original author or authors.
2+
* Copyright 2012-2024 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.
@@ -57,6 +57,11 @@ public class Cookie {
5757
*/
5858
private Boolean secure;
5959

60+
/**
61+
* Whether the generated cookie carries the Partitioned attribute.
62+
*/
63+
private Boolean partitioned;
64+
6065
/**
6166
* Maximum age of the cookie. If a duration suffix is not specified, seconds will be
6267
* used. A positive value indicates when the cookie expires relative to the current
@@ -127,6 +132,14 @@ public void setSameSite(SameSite sameSite) {
127132
this.sameSite = sameSite;
128133
}
129134

135+
public Boolean getPartitioned() {
136+
return this.partitioned;
137+
}
138+
139+
public void setPartitioned(Boolean partitioned) {
140+
this.partitioned = partitioned;
141+
}
142+
130143
/**
131144
* SameSite values.
132145
*/

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

+5
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@
6060
public abstract class AbstractServletWebServerFactory extends AbstractConfigurableWebServerFactory
6161
implements ConfigurableServletWebServerFactory {
6262

63+
static final String PARTITIONED_ATTRIBUTE_NAME = "Partitioned";
64+
6365
protected final Log logger = LogFactory.getLog(getClass());
6466

6567
private String contextPath = "";
@@ -350,6 +352,9 @@ private void configureSessionCookie(SessionCookieConfig config) {
350352
map.from(cookie::getHttpOnly).to(config::setHttpOnly);
351353
map.from(cookie::getSecure).to(config::setSecure);
352354
map.from(cookie::getMaxAge).asInt(Duration::getSeconds).to(config::setMaxAge);
355+
map.from(cookie::getPartitioned)
356+
.as(Object::toString)
357+
.to((partitioned) -> config.setAttribute(PARTITIONED_ATTRIBUTE_NAME, partitioned));
353358
}
354359

355360
private Set<jakarta.servlet.SessionTrackingMode> unwrap(Set<Session.SessionTrackingMode> modes) {

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

+6
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,7 @@ void sessionCookieConfiguration() {
863863
factory.getSession().getCookie().setPath("/testpath");
864864
factory.getSession().getCookie().setHttpOnly(true);
865865
factory.getSession().getCookie().setSecure(true);
866+
factory.getSession().getCookie().setPartitioned(true);
866867
factory.getSession().getCookie().setMaxAge(Duration.ofSeconds(60));
867868
final AtomicReference<SessionCookieConfig> configReference = new AtomicReference<>();
868869
this.webServer = factory.getWebServer((context) -> configReference.set(context.getSessionCookieConfig()));
@@ -872,6 +873,8 @@ void sessionCookieConfiguration() {
872873
assertThat(sessionCookieConfig.getPath()).isEqualTo("/testpath");
873874
assertThat(sessionCookieConfig.isHttpOnly()).isTrue();
874875
assertThat(sessionCookieConfig.isSecure()).isTrue();
876+
assertThat(sessionCookieConfig.getAttribute(AbstractServletWebServerFactory.PARTITIONED_ATTRIBUTE_NAME))
877+
.isEqualTo("true");
875878
assertThat(sessionCookieConfig.getMaxAge()).isEqualTo(60);
876879
}
877880

@@ -1166,6 +1169,7 @@ void sessionConfiguration() {
11661169
factory.getSession().getCookie().setPath("/testpath");
11671170
factory.getSession().getCookie().setHttpOnly(true);
11681171
factory.getSession().getCookie().setSecure(true);
1172+
factory.getSession().getCookie().setPartitioned(false);
11691173
factory.getSession().getCookie().setMaxAge(Duration.ofMinutes(1));
11701174
AtomicReference<ServletContext> contextReference = new AtomicReference<>();
11711175
factory.getWebServer(contextReference::set).start();
@@ -1178,6 +1182,8 @@ void sessionConfiguration() {
11781182
assertThat(servletContext.getSessionCookieConfig().isHttpOnly()).isTrue();
11791183
assertThat(servletContext.getSessionCookieConfig().isSecure()).isTrue();
11801184
assertThat(servletContext.getSessionCookieConfig().getMaxAge()).isEqualTo(60);
1185+
assertThat(servletContext.getSessionCookieConfig()
1186+
.getAttribute(AbstractServletWebServerFactory.PARTITIONED_ATTRIBUTE_NAME)).isEqualTo("false");
11811187
}
11821188

11831189
@Test

0 commit comments

Comments
 (0)