diff --git a/spring-web/src/main/java/org/springframework/http/ResponseCookie.java b/spring-web/src/main/java/org/springframework/http/ResponseCookie.java index b459e3a147..6c44f596ca 100644 --- a/spring-web/src/main/java/org/springframework/http/ResponseCookie.java +++ b/spring-web/src/main/java/org/springframework/http/ResponseCookie.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ import org.springframework.util.StringUtils; * static method. * * @author Rossen Stoyanchev + * @author Brian Clozel * @since 5.0 * @see RFC 6265 */ @@ -46,12 +47,15 @@ public final class ResponseCookie extends HttpCookie { private final boolean httpOnly; + @Nullable + private final String sameSite; + /** * Private constructor. See {@link #from(String, String)}. */ private ResponseCookie(String name, String value, Duration maxAge, @Nullable String domain, - @Nullable String path, boolean secure, boolean httpOnly) { + @Nullable String path, boolean secure, boolean httpOnly, @Nullable String sameSite) { super(name, value); Assert.notNull(maxAge, "Max age must not be null"); @@ -60,6 +64,7 @@ public final class ResponseCookie extends HttpCookie { this.path = path; this.secure = secure; this.httpOnly = httpOnly; + this.sameSite = sameSite; } @@ -105,6 +110,16 @@ public final class ResponseCookie extends HttpCookie { return this.httpOnly; } + /** + * Return the cookie "SameSite" attribute, or {@code null} if not set. + *
This limits the scope of the cookie such that it will only be attached to + * same site requests if {@code "Strict"} or cross-site requests if {@code "Lax"}. + * @see RFC6265 bis + */ + @Nullable + public String getSameSite() { + return this.sameSite; + } @Override public boolean equals(Object other) { @@ -146,13 +161,15 @@ public final class ResponseCookie extends HttpCookie { headers.setExpires(seconds > 0 ? System.currentTimeMillis() + seconds : 0); sb.append(headers.getFirst(HttpHeaders.EXPIRES)); } - if (this.secure) { sb.append("; Secure"); } if (this.httpOnly) { sb.append("; HttpOnly"); } + if (StringUtils.hasText(this.sameSite)) { + sb.append("; SameSite=").append(this.sameSite); + } return sb.toString(); } @@ -180,6 +197,9 @@ public final class ResponseCookie extends HttpCookie { private boolean httpOnly; + @Nullable + private String sameSite; + @Override public ResponseCookieBuilder maxAge(Duration maxAge) { this.maxAge = maxAge; @@ -216,10 +236,16 @@ public final class ResponseCookie extends HttpCookie { return this; } + @Override + public ResponseCookieBuilder sameSite(String sameSite) { + this.sameSite = sameSite; + return this; + } + @Override public ResponseCookie build() { return new ResponseCookie(name, value, this.maxAge, this.domain, this.path, - this.secure, this.httpOnly); + this.secure, this.httpOnly, this.sameSite); } }; } @@ -266,6 +292,12 @@ public final class ResponseCookie extends HttpCookie { */ ResponseCookieBuilder httpOnly(boolean httpOnly); + /** + * Add the "SameSite" attribute to the cookie. + * @see RFC6265 bis + */ + ResponseCookieBuilder sameSite(String sameSite); + /** * Create the HttpCookie. */ 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 c880504ae6..809552bfdb 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 @@ -31,6 +31,7 @@ import org.springframework.web.server.ServerWebExchange; * Cookie-based {@link WebSessionIdResolver}. * * @author Rossen Stoyanchev + * @author Brian Clozel * @since 5.0 */ public class CookieWebSessionIdResolver implements WebSessionIdResolver { @@ -39,6 +40,8 @@ public class CookieWebSessionIdResolver implements WebSessionIdResolver { private Duration cookieMaxAge = Duration.ofSeconds(-1); + private String sameSite = "Strict"; + /** * Set the name of the cookie to use for the session id. @@ -74,6 +77,23 @@ public class CookieWebSessionIdResolver implements WebSessionIdResolver { return this.cookieMaxAge; } + /** + * Set the value for the "SameSite" attribute of the cookie that holds the + * session id. For its meaning and possible values, see + * {@link ResponseCookie#getSameSite()}. + *
By default set to {@code "Strict"}
+ * @param sameSite the SameSite value
+ */
+ public void setSameSite(String sameSite) {
+ this.sameSite = sameSite;
+ }
+
+ /**
+ * Return the configured "SameSite" attribute value for the session cookie.
+ */
+ public String getSameSite() {
+ return sameSite;
+ }
@Override
public List