diff --git a/spring-web/src/main/java/org/springframework/web/server/session/CookieWebSessionIdResolver.java b/spring-web/src/main/java/org/springframework/web/server/session/CookieWebSessionIdResolver.java index 6a328f20a9..bd376274ac 100644 --- a/spring-web/src/main/java/org/springframework/web/server/session/CookieWebSessionIdResolver.java +++ b/spring-web/src/main/java/org/springframework/web/server/session/CookieWebSessionIdResolver.java @@ -19,6 +19,7 @@ package org.springframework.web.server.session; import java.time.Duration; import java.util.Collections; import java.util.List; +import java.util.function.Consumer; import java.util.stream.Collectors; import org.springframework.http.HttpCookie; @@ -43,6 +44,9 @@ public class CookieWebSessionIdResolver implements WebSessionIdResolver { private String sameSite = "Strict"; + @Nullable + private Consumer cookieInitializer = null; + /** * Set the name of the cookie to use for the session id. @@ -98,6 +102,19 @@ public class CookieWebSessionIdResolver implements WebSessionIdResolver { return this.sameSite; } + /** + * Add {@link Consumer} for a {@link ResponseCookie.ResponseCookieBuilder + * ResponseCookieBuilder} that will be invoked for each cookie being built, + * just before the call to + * {@link ResponseCookie.ResponseCookieBuilder#build() build()}. + * @param initializer consumer for a cookie builder + * @since 5.1 + */ + public void addCookieInitializer(Consumer initializer) { + this.cookieInitializer = this.cookieInitializer != null ? + this.cookieInitializer.andThen(initializer) : initializer; + } + @Override public List resolveSessionIds(ServerWebExchange exchange) { @@ -125,13 +142,18 @@ public class CookieWebSessionIdResolver implements WebSessionIdResolver { private ResponseCookie initSessionCookie( ServerWebExchange exchange, String id, Duration maxAge, @Nullable String sameSite) { - return ResponseCookie.from(this.cookieName, id) + ResponseCookie.ResponseCookieBuilder cookieBuilder = ResponseCookie.from(this.cookieName, id) .path(exchange.getRequest().getPath().contextPath().value() + "/") .maxAge(maxAge) .httpOnly(true) .secure("https".equalsIgnoreCase(exchange.getRequest().getURI().getScheme())) - .sameSite(sameSite) - .build(); + .sameSite(sameSite); + + if (this.cookieInitializer != null) { + this.cookieInitializer.accept(cookieBuilder); + } + + return cookieBuilder.build(); } } diff --git a/spring-web/src/test/java/org/springframework/web/server/session/CookieWebSessionIdResolverTests.java b/spring-web/src/test/java/org/springframework/web/server/session/CookieWebSessionIdResolverTests.java index f710ef4cdc..ab21cc2a04 100644 --- a/spring-web/src/test/java/org/springframework/web/server/session/CookieWebSessionIdResolverTests.java +++ b/spring-web/src/test/java/org/springframework/web/server/session/CookieWebSessionIdResolverTests.java @@ -46,4 +46,22 @@ public class CookieWebSessionIdResolverTests { assertNotNull(cookie); assertEquals("SESSION=123; Path=/; Secure; HttpOnly; SameSite=Strict", cookie.toString()); } + + @Test + public void cookieInitializer() { + this.resolver.addCookieInitializer(builder -> builder.domain("example.org")); + this.resolver.addCookieInitializer(builder -> builder.sameSite("Lax")); + this.resolver.addCookieInitializer(builder -> builder.secure(false)); + + MockServerHttpRequest request = MockServerHttpRequest.get("https://example.org/path").build(); + MockServerWebExchange exchange = MockServerWebExchange.from(request); + this.resolver.setSessionId(exchange, "123"); + + MultiValueMap cookies = exchange.getResponse().getCookies(); + assertEquals(1, cookies.size()); + ResponseCookie cookie = cookies.getFirst(this.resolver.getCookieName()); + assertNotNull(cookie); + assertEquals("SESSION=123; Path=/; Domain=example.org; HttpOnly; SameSite=Lax", cookie.toString()); + } + }