diff --git a/web/src/main/java/org/springframework/security/web/csrf/CookieCsrfTokenRepository.java b/web/src/main/java/org/springframework/security/web/csrf/CookieCsrfTokenRepository.java index 473dbaf56c..d04a62ce89 100644 --- a/web/src/main/java/org/springframework/security/web/csrf/CookieCsrfTokenRepository.java +++ b/web/src/main/java/org/springframework/security/web/csrf/CookieCsrfTokenRepository.java @@ -57,6 +57,8 @@ public final class CookieCsrfTokenRepository implements CsrfTokenRepository { private Boolean secure; + private int cookieMaxAge = -1; + public CookieCsrfTokenRepository() { } @@ -71,7 +73,7 @@ public final class CookieCsrfTokenRepository implements CsrfTokenRepository { Cookie cookie = new Cookie(this.cookieName, tokenValue); cookie.setSecure((this.secure != null) ? this.secure : request.isSecure()); cookie.setPath(StringUtils.hasLength(this.cookiePath) ? this.cookiePath : this.getRequestContext(request)); - cookie.setMaxAge((token != null) ? -1 : 0); + cookie.setMaxAge((token != null) ? this.cookieMaxAge : 0); cookie.setHttpOnly(this.cookieHttpOnly); if (StringUtils.hasLength(this.cookieDomain)) { cookie.setDomain(this.cookieDomain); @@ -192,4 +194,30 @@ public final class CookieCsrfTokenRepository implements CsrfTokenRepository { this.secure = secure; } + /** + * Sets maximum age in seconds for the cookie that the expected CSRF token is saved to + * and read from. By default maximum age value is -1. + * + *

+ * A positive value indicates that the cookie will expire after that many seconds have + * passed. Note that the value is the maximum age when the cookie will expire, + * not the cookie's current age. + * + *

+ * A negative value means that the cookie is not stored persistently and will be + * deleted when the Web browser exits. + * + *

+ * A zero value causes the cookie to be deleted immediately therefore it is not a + * valid value and in that case an {@link IllegalArgumentException} will be thrown. + * @param cookieMaxAge an integer specifying the maximum age of the cookie in seconds; + * if negative, means the cookie is not stored; if zero, the method throws an + * {@link IllegalArgumentException} + * @since 5.5 + */ + public void setCookieMaxAge(int cookieMaxAge) { + Assert.isTrue(cookieMaxAge != 0, "cookieMaxAge is not zero"); + this.cookieMaxAge = cookieMaxAge; + } + } diff --git a/web/src/test/java/org/springframework/security/web/csrf/CookieCsrfTokenRepositoryTests.java b/web/src/test/java/org/springframework/security/web/csrf/CookieCsrfTokenRepositoryTests.java index e204be126b..722bc01a69 100644 --- a/web/src/test/java/org/springframework/security/web/csrf/CookieCsrfTokenRepositoryTests.java +++ b/web/src/test/java/org/springframework/security/web/csrf/CookieCsrfTokenRepositoryTests.java @@ -190,6 +190,16 @@ public class CookieCsrfTokenRepositoryTests { assertThat(tokenCookie.getDomain()).isEqualTo(domainName); } + @Test + public void saveTokenWithCookieMaxAge() { + int maxAge = 1200; + this.repository.setCookieMaxAge(maxAge); + CsrfToken token = this.repository.generateToken(this.request); + this.repository.saveToken(token, this.request, this.response); + Cookie tokenCookie = this.response.getCookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME); + assertThat(tokenCookie.getMaxAge()).isEqualTo(maxAge); + } + @Test public void loadTokenNoCookiesNull() { assertThat(this.repository.loadToken(this.request)).isNull(); @@ -251,4 +261,9 @@ public class CookieCsrfTokenRepositoryTests { assertThatIllegalArgumentException().isThrownBy(() -> this.repository.setHeaderName(null)); } + @Test + public void setCookieMaxAgeZeroIllegalArgumentException() { + assertThatIllegalArgumentException().isThrownBy(() -> this.repository.setCookieMaxAge(0)); + } + }