瀏覽代碼

Cache Mono that generate the CSRF token

Closes gh-9113
Tomoki Tsubaki 4 年之前
父節點
當前提交
0c8b6df82a

+ 1 - 1
web/src/main/java/org/springframework/security/web/server/csrf/CsrfWebFilter.java

@@ -191,7 +191,7 @@ public class CsrfWebFilter implements WebFilter {
 
 	private Mono<CsrfToken> generateToken(ServerWebExchange exchange) {
 		return this.csrfTokenRepository.generateToken(exchange)
-				.delayUntil((token) -> this.csrfTokenRepository.saveToken(exchange, token));
+				.delayUntil((token) -> this.csrfTokenRepository.saveToken(exchange, token)).cache();
 	}
 
 	private static class DefaultRequireCsrfProtectionMatcher implements ServerWebExchangeMatcher {

+ 23 - 0
web/src/test/java/org/springframework/security/web/server/csrf/CsrfWebFilterTests.java

@@ -16,6 +16,9 @@
 
 package org.springframework.security.web.server.csrf;
 
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -227,6 +230,26 @@ public class CsrfWebFilterTests {
 				.isForbidden();
 	}
 
+	// gh-9113
+	@Test
+	public void filterWhenSubscribingCsrfTokenMultipleTimesThenGenerateOnlyOnce() {
+		this.csrfFilter.setCsrfTokenRepository(this.repository);
+		given(this.repository.loadToken(any())).willReturn(Mono.empty());
+		AtomicInteger count = new AtomicInteger();
+		given(this.repository.generateToken(any())).willReturn(Mono.fromCallable(() -> {
+			count.incrementAndGet();
+			return this.token;
+		}));
+		given(this.repository.saveToken(any(), any())).willReturn(Mono.empty());
+		AtomicReference<Mono<CsrfToken>> tokenFromExchange = new AtomicReference<>();
+		given(this.chain.filter(any())).willReturn(
+				Mono.fromRunnable(() -> tokenFromExchange.set(this.get.getAttribute(CsrfToken.class.getName()))));
+		this.csrfFilter.filter(this.get, this.chain).block();
+		tokenFromExchange.get().block();
+		tokenFromExchange.get().block();
+		assertThat(count).hasValue(1);
+	}
+
 	@RestController
 	static class OkController {