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