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
This commit is contained in:
commit
437c259d9e
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2012-2024 the original author or authors.
|
* Copyright 2012-2025 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -77,12 +77,7 @@ public class WebSessionIdResolverAutoConfiguration {
|
||||||
map.from(cookie::getSecure).to(builder::secure);
|
map.from(cookie::getSecure).to(builder::secure);
|
||||||
map.from(cookie::getMaxAge).to(builder::maxAge);
|
map.from(cookie::getMaxAge).to(builder::maxAge);
|
||||||
map.from(cookie::getPartitioned).to(builder::partitioned);
|
map.from(cookie::getPartitioned).to(builder::partitioned);
|
||||||
map.from(getSameSite(cookie)).to(builder::sameSite);
|
map.from(cookie::getSameSite).as(SameSite::attributeValue).to(builder::sameSite);
|
||||||
}
|
|
||||||
|
|
||||||
private String getSameSite(Cookie properties) {
|
|
||||||
SameSite sameSite = properties.getSameSite();
|
|
||||||
return (sameSite != null) ? sameSite.attributeValue() : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2012-2024 the original author or authors.
|
* Copyright 2012-2025 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -170,6 +170,16 @@ class SessionAutoConfigurationTests extends AbstractSessionAutoConfigurationTest
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void sessionCookieSameSiteOmittedIsAppliedToAutoConfiguredCookieSerializer() {
|
||||||
|
this.contextRunner.withUserConfiguration(SessionRepositoryConfiguration.class)
|
||||||
|
.withPropertyValues("server.servlet.session.cookie.sameSite=omitted")
|
||||||
|
.run((context) -> {
|
||||||
|
DefaultCookieSerializer cookieSerializer = context.getBean(DefaultCookieSerializer.class);
|
||||||
|
assertThat(cookieSerializer).hasFieldOrPropertyWithValue("sameSite", null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void autoConfiguredCookieSerializerIsUsedBySessionRepositoryFilter() {
|
void autoConfiguredCookieSerializerIsUsedBySessionRepositoryFilter() {
|
||||||
this.contextRunner.withUserConfiguration(SessionRepositoryConfiguration.class)
|
this.contextRunner.withUserConfiguration(SessionRepositoryConfiguration.class)
|
||||||
|
|
|
@ -676,6 +676,15 @@ class WebFluxAutoConfigurationTests {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void sessionCookieOmittedConfigurationShouldBeApplied() {
|
||||||
|
this.contextRunner.withPropertyValues("server.reactive.session.cookie.same-site:omitted")
|
||||||
|
.run(assertExchangeWithSession((exchange) -> {
|
||||||
|
List<ResponseCookie> cookies = exchange.getResponse().getCookies().get("SESSION");
|
||||||
|
assertThat(cookies).extracting(ResponseCookie::getSameSite).containsOnlyNulls();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ValueSource(classes = { ServerProperties.class, WebFluxProperties.class })
|
@ValueSource(classes = { ServerProperties.class, WebFluxProperties.class })
|
||||||
void propertiesAreNotEnabledInNonWebApplication(Class<?> propertiesClass) {
|
void propertiesAreNotEnabledInNonWebApplication(Class<?> propertiesClass) {
|
||||||
|
|
|
@ -284,7 +284,7 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor
|
||||||
private void configureSession(WebAppContext context) {
|
private void configureSession(WebAppContext context) {
|
||||||
SessionHandler handler = context.getSessionHandler();
|
SessionHandler handler = context.getSessionHandler();
|
||||||
SameSite sessionSameSite = getSession().getCookie().getSameSite();
|
SameSite sessionSameSite = getSession().getCookie().getSameSite();
|
||||||
if (sessionSameSite != null) {
|
if (sessionSameSite != null && sessionSameSite != SameSite.OMITTED) {
|
||||||
handler.setSameSite(HttpCookie.SameSite.valueOf(sessionSameSite.name()));
|
handler.setSameSite(HttpCookie.SameSite.valueOf(sessionSameSite.name()));
|
||||||
}
|
}
|
||||||
Duration sessionTimeout = getSession().getTimeout();
|
Duration sessionTimeout = getSession().getTimeout();
|
||||||
|
|
|
@ -998,11 +998,12 @@ public class TomcatServletWebServerFactory extends AbstractServletWebServerFacto
|
||||||
@Override
|
@Override
|
||||||
public String generateHeader(Cookie cookie, HttpServletRequest request) {
|
public String generateHeader(Cookie cookie, HttpServletRequest request) {
|
||||||
SameSite sameSite = getSameSite(cookie);
|
SameSite sameSite = getSameSite(cookie);
|
||||||
if (sameSite == null) {
|
String sameSiteValue = (sameSite != null) ? sameSite.attributeValue() : null;
|
||||||
|
if (sameSiteValue == null) {
|
||||||
return super.generateHeader(cookie, request);
|
return super.generateHeader(cookie, request);
|
||||||
}
|
}
|
||||||
Rfc6265CookieProcessor delegate = new Rfc6265CookieProcessor();
|
Rfc6265CookieProcessor delegate = new Rfc6265CookieProcessor();
|
||||||
delegate.setSameSiteCookies(sameSite.attributeValue());
|
delegate.setSameSiteCookies(sameSiteValue);
|
||||||
return delegate.generateHeader(cookie, request);
|
return delegate.generateHeader(cookie, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -635,7 +635,10 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac
|
||||||
private void beforeCommit(HttpServerExchange exchange) {
|
private void beforeCommit(HttpServerExchange exchange) {
|
||||||
for (Cookie cookie : exchange.responseCookies()) {
|
for (Cookie cookie : exchange.responseCookies()) {
|
||||||
SameSite sameSite = getSameSite(asServletCookie(cookie));
|
SameSite sameSite = getSameSite(asServletCookie(cookie));
|
||||||
if (sameSite != null) {
|
if (sameSite == SameSite.OMITTED) {
|
||||||
|
cookie.setSameSite(false);
|
||||||
|
}
|
||||||
|
else if (sameSite != null) {
|
||||||
cookie.setSameSiteMode(sameSite.attributeValue());
|
cookie.setSameSiteMode(sameSite.attributeValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2012-2024 the original author or authors.
|
* Copyright 2012-2025 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -146,19 +146,25 @@ public class Cookie {
|
||||||
public enum SameSite {
|
public enum SameSite {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cookies are sent in both first-party and cross-origin requests.
|
* SameSite attribute will be omitted when creating the cookie.
|
||||||
|
*/
|
||||||
|
OMITTED(null),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SameSite attribute will be set to None. Cookies are sent in both first-party
|
||||||
|
* and cross-origin requests.
|
||||||
*/
|
*/
|
||||||
NONE("None"),
|
NONE("None"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cookies are sent in a first-party context, also when following a link to the
|
* SameSite attribute will be set to Lax. Cookies are sent in a first-party
|
||||||
* origin site.
|
* context, also when following a link to the origin site.
|
||||||
*/
|
*/
|
||||||
LAX("Lax"),
|
LAX("Lax"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cookies are only sent in a first-party context (i.e. not when following a link
|
* SameSite attribute will be set to Strict. Cookies are only sent in a
|
||||||
* to the origin site).
|
* first-party context (i.e. not when following a link to the origin site).
|
||||||
*/
|
*/
|
||||||
STRICT("Strict");
|
STRICT("Strict");
|
||||||
|
|
||||||
|
|
|
@ -881,7 +881,7 @@ public abstract class AbstractServletWebServerFactoryTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@EnumSource
|
@EnumSource(mode = EnumSource.Mode.EXCLUDE, names = "OMITTED")
|
||||||
void sessionCookieSameSiteAttributeCanBeConfiguredAndOnlyAffectsSessionCookies(SameSite sameSite) throws Exception {
|
void sessionCookieSameSiteAttributeCanBeConfiguredAndOnlyAffectsSessionCookies(SameSite sameSite) throws Exception {
|
||||||
AbstractServletWebServerFactory factory = getFactory();
|
AbstractServletWebServerFactory factory = getFactory();
|
||||||
factory.getSession().getCookie().setSameSite(sameSite);
|
factory.getSession().getCookie().setSameSite(sameSite);
|
||||||
|
@ -896,7 +896,7 @@ public abstract class AbstractServletWebServerFactoryTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@EnumSource
|
@EnumSource(mode = EnumSource.Mode.EXCLUDE, names = "OMITTED")
|
||||||
void sessionCookieSameSiteAttributeCanBeConfiguredAndOnlyAffectsSessionCookiesWhenUsingCustomName(SameSite sameSite)
|
void sessionCookieSameSiteAttributeCanBeConfiguredAndOnlyAffectsSessionCookiesWhenUsingCustomName(SameSite sameSite)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
AbstractServletWebServerFactory factory = getFactory();
|
AbstractServletWebServerFactory factory = getFactory();
|
||||||
|
@ -949,6 +949,23 @@ public abstract class AbstractServletWebServerFactoryTests {
|
||||||
(header) -> assertThat(header).contains("test=test").contains("SameSite=Strict"));
|
(header) -> assertThat(header).contains("test=test").contains("SameSite=Strict"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void cookieSameSiteSuppliersShouldNotAffectOmittedSameSite() throws IOException, URISyntaxException {
|
||||||
|
AbstractServletWebServerFactory factory = getFactory();
|
||||||
|
factory.getSession().getCookie().setSameSite(SameSite.OMITTED);
|
||||||
|
factory.getSession().getCookie().setName("SESSIONCOOKIE");
|
||||||
|
factory.addCookieSameSiteSuppliers(CookieSameSiteSupplier.ofStrict());
|
||||||
|
factory.addInitializers(new ServletRegistrationBean<>(new CookieServlet(false), "/"));
|
||||||
|
this.webServer = factory.getWebServer();
|
||||||
|
this.webServer.start();
|
||||||
|
ClientHttpResponse clientResponse = getClientResponse(getLocalUrl("/"));
|
||||||
|
assertThat(clientResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
|
List<String> setCookieHeaders = clientResponse.getHeaders().get("Set-Cookie");
|
||||||
|
assertThat(setCookieHeaders).satisfiesExactlyInAnyOrder(
|
||||||
|
(header) -> assertThat(header).contains("SESSIONCOOKIE").doesNotContain("SameSite"),
|
||||||
|
(header) -> assertThat(header).contains("test=test").contains("SameSite=Strict"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
protected void sslSessionTracking() {
|
protected void sslSessionTracking() {
|
||||||
AbstractServletWebServerFactory factory = getFactory();
|
AbstractServletWebServerFactory factory = getFactory();
|
||||||
|
|
Loading…
Reference in New Issue