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 c275247c72..7b17277c5a 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 @@ -53,6 +53,8 @@ public final class CookieCsrfTokenRepository implements CsrfTokenRepository { private boolean cookieHttpOnly; + private String cookiePath; + public CookieCsrfTokenRepository() { this.setHttpOnlyMethod = ReflectionUtils.findMethod(Cookie.class, "setHttpOnly", boolean.class); if (this.setHttpOnlyMethod != null) { @@ -72,7 +74,11 @@ public final class CookieCsrfTokenRepository implements CsrfTokenRepository { String tokenValue = token == null ? "" : token.getToken(); Cookie cookie = new Cookie(this.cookieName, tokenValue); cookie.setSecure(request.isSecure()); - cookie.setPath(getCookiePath(request)); + if (this.cookiePath != null && !this.cookiePath.isEmpty()) { + cookie.setPath(this.cookiePath); + } else { + cookie.setPath(this.getRequestContext(request)); + } if (token == null) { cookie.setMaxAge(0); } @@ -148,7 +154,7 @@ public final class CookieCsrfTokenRepository implements CsrfTokenRepository { this.cookieHttpOnly = cookieHttpOnly; } - private String getCookiePath(HttpServletRequest request) { + private String getRequestContext(HttpServletRequest request) { String contextPath = request.getContextPath(); return contextPath.length() > 0 ? contextPath : "/"; } @@ -169,4 +175,23 @@ public final class CookieCsrfTokenRepository implements CsrfTokenRepository { private String createNewToken() { return UUID.randomUUID().toString(); } -} \ No newline at end of file + + /** + * Set the path that the Cookie will be created with. This will will override the default functionality which uses the + * request context as the path. + * + * @param path the path to use + */ + public void setCookiePath(String path) { + this.cookiePath = path; + } + + /** + * Get the path that the CSRF cookie will be set to. + * + * @return the path to be used. + */ + public String getCookiePath() { + return this.cookiePath; + } +} 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 6a19774fda..9a66dafb6e 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 @@ -150,6 +150,45 @@ public class CookieCsrfTokenRepositoryTests { assertThat(tokenCookie.isHttpOnly()).isFalse(); } + @Test + public void saveTokenCustomPath() { + String customPath = "/custompath"; + this.repository.setCookiePath(customPath); + 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.getPath()).isEqualTo(this.repository.getCookiePath()); + } + + @Test + public void saveTokenEmptyCustomPath() { + String customPath = ""; + this.repository.setCookiePath(customPath); + 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.getPath()).isEqualTo(this.request.getContextPath()); + } + + @Test + public void saveTokenNullCustomPath() { + String customPath = null; + this.repository.setCookiePath(customPath); + 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.getPath()).isEqualTo(this.request.getContextPath()); + } + @Test public void loadTokenNoCookiesNull() { assertThat(this.repository.loadToken(this.request)).isNull();