Skip to content

Commit 36f668d

Browse files
author
Steve Riesenberg
committed
Merge branch '5.8.x'
Closes gh-12142
2 parents ee634cf + 6b0ed02 commit 36f668d

File tree

2 files changed

+43
-0
lines changed

2 files changed

+43
-0
lines changed

web/src/main/java/org/springframework/security/web/csrf/CookieCsrfTokenRepository.java

+17
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ public final class CookieCsrfTokenRepository implements CsrfTokenRepository {
4343

4444
static final String DEFAULT_CSRF_HEADER_NAME = "X-XSRF-TOKEN";
4545

46+
private static final String CSRF_TOKEN_REMOVED_ATTRIBUTE_NAME = CookieCsrfTokenRepository.class.getName()
47+
.concat(".REMOVED");
48+
4649
private String parameterName = DEFAULT_CSRF_PARAMETER_NAME;
4750

4851
private String headerName = DEFAULT_CSRF_HEADER_NAME;
@@ -79,10 +82,24 @@ public void saveToken(CsrfToken token, HttpServletRequest request, HttpServletRe
7982
cookie.setDomain(this.cookieDomain);
8083
}
8184
response.addCookie(cookie);
85+
86+
// Set request attribute to signal that response has blank cookie value,
87+
// which allows loadToken to return null when token has been removed
88+
if (!StringUtils.hasLength(tokenValue)) {
89+
request.setAttribute(CSRF_TOKEN_REMOVED_ATTRIBUTE_NAME, Boolean.TRUE);
90+
}
91+
else {
92+
request.removeAttribute(CSRF_TOKEN_REMOVED_ATTRIBUTE_NAME);
93+
}
8294
}
8395

8496
@Override
8597
public CsrfToken loadToken(HttpServletRequest request) {
98+
// Return null when token has been removed during the current request
99+
// which allows loadDeferredToken to re-generate the token
100+
if (Boolean.TRUE.equals(request.getAttribute(CSRF_TOKEN_REMOVED_ATTRIBUTE_NAME))) {
101+
return null;
102+
}
86103
Cookie cookie = WebUtils.getCookie(request, this.cookieName);
87104
if (cookie == null) {
88105
return null;

web/src/test/java/org/springframework/security/web/csrf/CookieCsrfTokenRepositoryTests.java

+26
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,32 @@ public void loadDeferredTokenWhenDoesNotExistThenGeneratedAndSaved() {
262262
assertThat(tokenCookie.isHttpOnly()).isEqualTo(true);
263263
}
264264

265+
@Test
266+
public void loadDeferredTokenWhenExistsAndNullSavedThenGeneratedAndSaved() {
267+
CsrfToken generatedToken = this.repository.generateToken(this.request);
268+
this.request
269+
.setCookies(new Cookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME, generatedToken.getToken()));
270+
this.repository.saveToken(null, this.request, this.response);
271+
DeferredCsrfToken deferredCsrfToken = this.repository.loadDeferredToken(this.request, this.response);
272+
CsrfToken csrfToken = deferredCsrfToken.get();
273+
assertThat(csrfToken).isNotNull();
274+
assertThat(generatedToken).isNotEqualTo(csrfToken);
275+
assertThat(deferredCsrfToken.isGenerated()).isTrue();
276+
}
277+
278+
@Test
279+
public void loadDeferredTokenWhenExistsAndNullSavedAndNonNullSavedThenLoaded() {
280+
CsrfToken generatedToken = this.repository.generateToken(this.request);
281+
this.request
282+
.setCookies(new Cookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME, generatedToken.getToken()));
283+
this.repository.saveToken(null, this.request, this.response);
284+
this.repository.saveToken(generatedToken, this.request, this.response);
285+
DeferredCsrfToken deferredCsrfToken = this.repository.loadDeferredToken(this.request, this.response);
286+
CsrfToken csrfToken = deferredCsrfToken.get();
287+
assertThatCsrfToken(csrfToken).isEqualTo(generatedToken);
288+
assertThat(deferredCsrfToken.isGenerated()).isFalse();
289+
}
290+
265291
@Test
266292
public void loadDeferredTokenWhenExistsThenLoaded() {
267293
CsrfToken generatedToken = this.repository.generateToken(this.request);

0 commit comments

Comments
 (0)