Skip to content

Commit 05e4a1d

Browse files
author
Steve Riesenberg
committed
Cache Xor CsrfToken
Closes gh-11988
1 parent ffbcaca commit 05e4a1d

File tree

4 files changed

+44
-3
lines changed

4 files changed

+44
-3
lines changed

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

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,12 @@ public void handle(HttpServletRequest request, HttpServletResponse response,
5959
}
6060

6161
private Supplier<CsrfToken> deferCsrfTokenUpdate(Supplier<CsrfToken> csrfTokenSupplier) {
62-
return () -> {
62+
return new CachedCsrfTokenSupplier(() -> {
6363
CsrfToken csrfToken = csrfTokenSupplier.get();
6464
Assert.state(csrfToken != null, "csrfToken supplier returned null");
6565
String updatedToken = createXoredCsrfToken(this.secureRandom, csrfToken.getToken());
6666
return new DefaultCsrfToken(csrfToken.getHeaderName(), csrfToken.getParameterName(), updatedToken);
67-
};
67+
});
6868
}
6969

7070
@Override
@@ -123,4 +123,24 @@ private static byte[] xorCsrf(byte[] randomBytes, byte[] csrfBytes) {
123123
return xoredCsrf;
124124
}
125125

126+
private static final class CachedCsrfTokenSupplier implements Supplier<CsrfToken> {
127+
128+
private final Supplier<CsrfToken> delegate;
129+
130+
private CsrfToken csrfToken;
131+
132+
private CachedCsrfTokenSupplier(Supplier<CsrfToken> delegate) {
133+
this.delegate = delegate;
134+
}
135+
136+
@Override
137+
public CsrfToken get() {
138+
if (this.csrfToken == null) {
139+
this.csrfToken = this.delegate.get();
140+
}
141+
return this.csrfToken;
142+
}
143+
144+
}
145+
126146
}

web/src/main/java/org/springframework/security/web/server/csrf/XorServerCsrfTokenRequestAttributeHandler.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ public void handle(ServerWebExchange exchange, Mono<CsrfToken> csrfToken) {
5353
Assert.notNull(exchange, "exchange cannot be null");
5454
Assert.notNull(csrfToken, "csrfToken cannot be null");
5555
Mono<CsrfToken> updatedCsrfToken = csrfToken.map((token) -> new DefaultCsrfToken(token.getHeaderName(),
56-
token.getParameterName(), createXoredCsrfToken(this.secureRandom, token.getToken())));
56+
token.getParameterName(), createXoredCsrfToken(this.secureRandom, token.getToken())))
57+
.cast(CsrfToken.class).cache();
5758
super.handle(exchange, updatedCsrfToken);
5859
}
5960

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,15 @@ public void handleWhenValidParametersThenRequestAttributesSet() {
148148
assertThat(csrfTokenAttribute.getToken()).isEqualTo(XOR_CSRF_TOKEN_VALUE);
149149
}
150150

151+
@Test
152+
public void handleWhenCsrfTokenRequestedTwiceThenCached() {
153+
this.handler.handle(this.request, this.response, () -> this.token);
154+
155+
CsrfToken csrfTokenAttribute = (CsrfToken) this.request.getAttribute(CsrfToken.class.getName());
156+
assertThat(csrfTokenAttribute.getToken()).isNotEqualTo(this.token.getToken());
157+
assertThat(csrfTokenAttribute.getToken()).isEqualTo(csrfTokenAttribute.getToken());
158+
}
159+
151160
@Test
152161
public void resolveCsrfTokenValueWhenRequestIsNullThenThrowsIllegalArgumentException() {
153162
assertThatIllegalArgumentException().isThrownBy(() -> this.handler.resolveCsrfTokenValue(null, this.token))

web/src/test/java/org/springframework/security/web/server/csrf/XorServerCsrfTokenRequestAttributeHandlerTests.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,17 @@ public void handleWhenValidParametersThenExchangeAttributeSet() {
110110
verify(this.secureRandom).nextBytes(anyByteArray());
111111
}
112112

113+
@Test
114+
public void handleWhenCsrfTokenRequestedTwiceThenCached() {
115+
this.handler.handle(this.exchange, Mono.just(this.token));
116+
Mono<CsrfToken> csrfTokenAttribute = this.exchange.getAttribute(CsrfToken.class.getName());
117+
assertThat(csrfTokenAttribute).isNotNull();
118+
CsrfToken csrfToken1 = csrfTokenAttribute.block();
119+
CsrfToken csrfToken2 = csrfTokenAttribute.block();
120+
assertThat(csrfToken1.getToken()).isNotEqualTo(this.token.getToken());
121+
assertThat(csrfToken1.getToken()).isEqualTo(csrfToken2.getToken());
122+
}
123+
113124
@Test
114125
public void resolveCsrfTokenValueWhenExchangeIsNullThenThrowsIllegalArgumentException() {
115126
assertThatIllegalArgumentException().isThrownBy(() -> this.handler.resolveCsrfTokenValue(null, this.token))

0 commit comments

Comments
 (0)