Skip to content

Commit 8d09a9a

Browse files
committed
Create CsrfCustomizer for SPA configuration (closes #14149)
Signed-off-by: Felix Hagemans <[email protected]>
1 parent 39b195c commit 8d09a9a

File tree

2 files changed

+206
-106
lines changed

2 files changed

+206
-106
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package org.springframework.security.config.annotation.web.configurers;
2+
3+
import jakarta.servlet.http.HttpServletRequest;
4+
import jakarta.servlet.http.HttpServletResponse;
5+
import org.springframework.security.config.Customizer;
6+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
7+
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
8+
import org.springframework.security.web.csrf.CsrfToken;
9+
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
10+
import org.springframework.security.web.csrf.CsrfTokenRequestHandler;
11+
import org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler;
12+
import org.springframework.util.StringUtils;
13+
14+
import java.util.function.Supplier;
15+
16+
public class CsrfCustomizer {
17+
public static Customizer<CsrfConfigurer<HttpSecurity>> spaDefaults() {
18+
return (csrf) -> csrf
19+
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
20+
.csrfTokenRequestHandler(new SpaCsrfTokenRequestHandler());
21+
}
22+
23+
private static class SpaCsrfTokenRequestHandler implements CsrfTokenRequestHandler {
24+
private final CsrfTokenRequestHandler plain = new CsrfTokenRequestAttributeHandler();
25+
private final CsrfTokenRequestHandler xor = new XorCsrfTokenRequestAttributeHandler();
26+
27+
@Override
28+
public void handle(HttpServletRequest request, HttpServletResponse response, Supplier<CsrfToken> csrfToken) {
29+
/*
30+
* Always use XorCsrfTokenRequestAttributeHandler to provide BREACH protection of
31+
* the CsrfToken when it is rendered in the response body.
32+
*/
33+
this.xor.handle(request, response, csrfToken);
34+
/*
35+
* Render the token value to a cookie by causing the deferred token to be loaded.
36+
*/
37+
csrfToken.get();
38+
}
39+
40+
@Override
41+
public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {
42+
String headerValue = request.getHeader(csrfToken.getHeaderName());
43+
/*
44+
* If the request contains a request header, use CsrfTokenRequestAttributeHandler
45+
* to resolve the CsrfToken. This applies when a single-page application includes
46+
* the header value automatically, which was obtained via a cookie containing the
47+
* raw CsrfToken.
48+
*
49+
* In all other cases (e.g. if the request contains a request parameter), use
50+
* XorCsrfTokenRequestAttributeHandler to resolve the CsrfToken. This applies
51+
* when a server-side rendered form includes the _csrf request parameter as a
52+
* hidden input.
53+
*/
54+
return (StringUtils.hasText(headerValue) ? this.plain : this.xor).resolveCsrfTokenValue(request, csrfToken);
55+
}
56+
}
57+
}

0 commit comments

Comments
 (0)