|
@@ -19,9 +19,11 @@ package org.springframework.security.config.annotation.web.configurers;
|
|
import java.util.ArrayList;
|
|
import java.util.ArrayList;
|
|
import java.util.LinkedHashMap;
|
|
import java.util.LinkedHashMap;
|
|
import java.util.List;
|
|
import java.util.List;
|
|
|
|
+import java.util.function.Supplier;
|
|
|
|
|
|
import io.micrometer.observation.ObservationRegistry;
|
|
import io.micrometer.observation.ObservationRegistry;
|
|
import jakarta.servlet.http.HttpServletRequest;
|
|
import jakarta.servlet.http.HttpServletRequest;
|
|
|
|
+import jakarta.servlet.http.HttpServletResponse;
|
|
|
|
|
|
import org.springframework.context.ApplicationContext;
|
|
import org.springframework.context.ApplicationContext;
|
|
import org.springframework.security.access.AccessDeniedException;
|
|
import org.springframework.security.access.AccessDeniedException;
|
|
@@ -34,13 +36,17 @@ import org.springframework.security.web.access.CompositeAccessDeniedHandler;
|
|
import org.springframework.security.web.access.DelegatingAccessDeniedHandler;
|
|
import org.springframework.security.web.access.DelegatingAccessDeniedHandler;
|
|
import org.springframework.security.web.access.ObservationMarkingAccessDeniedHandler;
|
|
import org.springframework.security.web.access.ObservationMarkingAccessDeniedHandler;
|
|
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
|
|
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
|
|
|
|
+import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
|
|
import org.springframework.security.web.csrf.CsrfAuthenticationStrategy;
|
|
import org.springframework.security.web.csrf.CsrfAuthenticationStrategy;
|
|
import org.springframework.security.web.csrf.CsrfFilter;
|
|
import org.springframework.security.web.csrf.CsrfFilter;
|
|
import org.springframework.security.web.csrf.CsrfLogoutHandler;
|
|
import org.springframework.security.web.csrf.CsrfLogoutHandler;
|
|
|
|
+import org.springframework.security.web.csrf.CsrfToken;
|
|
import org.springframework.security.web.csrf.CsrfTokenRepository;
|
|
import org.springframework.security.web.csrf.CsrfTokenRepository;
|
|
|
|
+import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
|
|
import org.springframework.security.web.csrf.CsrfTokenRequestHandler;
|
|
import org.springframework.security.web.csrf.CsrfTokenRequestHandler;
|
|
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
|
|
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
|
|
import org.springframework.security.web.csrf.MissingCsrfTokenException;
|
|
import org.springframework.security.web.csrf.MissingCsrfTokenException;
|
|
|
|
+import org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler;
|
|
import org.springframework.security.web.session.InvalidSessionAccessDeniedHandler;
|
|
import org.springframework.security.web.session.InvalidSessionAccessDeniedHandler;
|
|
import org.springframework.security.web.session.InvalidSessionStrategy;
|
|
import org.springframework.security.web.session.InvalidSessionStrategy;
|
|
import org.springframework.security.web.util.matcher.AndRequestMatcher;
|
|
import org.springframework.security.web.util.matcher.AndRequestMatcher;
|
|
@@ -48,6 +54,7 @@ import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
|
|
import org.springframework.security.web.util.matcher.OrRequestMatcher;
|
|
import org.springframework.security.web.util.matcher.OrRequestMatcher;
|
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
|
import org.springframework.util.Assert;
|
|
import org.springframework.util.Assert;
|
|
|
|
+import org.springframework.util.StringUtils;
|
|
|
|
|
|
/**
|
|
/**
|
|
* Adds
|
|
* Adds
|
|
@@ -214,6 +221,21 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
|
|
return this;
|
|
return this;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * <p>
|
|
|
|
+ * Sensible CSRF defaults when used in combination with a single page application.
|
|
|
|
+ * Creates a cookie-based token repository and a custom request handler to resolve the
|
|
|
|
+ * actual token value instead of the encoded token.
|
|
|
|
+ * </p>
|
|
|
|
+ * @return the {@link CsrfConfigurer} for further customizations
|
|
|
|
+ * @since 7.0
|
|
|
|
+ */
|
|
|
|
+ public CsrfConfigurer<H> spa() {
|
|
|
|
+ this.csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
|
|
|
|
+ this.requestHandler = new SpaCsrfTokenRequestHandler();
|
|
|
|
+ return this;
|
|
|
|
+ }
|
|
|
|
+
|
|
@SuppressWarnings("unchecked")
|
|
@SuppressWarnings("unchecked")
|
|
@Override
|
|
@Override
|
|
public void configure(H http) {
|
|
public void configure(H http) {
|
|
@@ -375,4 +397,42 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ private static class SpaCsrfTokenRequestHandler implements CsrfTokenRequestHandler {
|
|
|
|
+
|
|
|
|
+ private final CsrfTokenRequestAttributeHandler plain = new CsrfTokenRequestAttributeHandler();
|
|
|
|
+
|
|
|
|
+ private final CsrfTokenRequestAttributeHandler xor = new XorCsrfTokenRequestAttributeHandler();
|
|
|
|
+
|
|
|
|
+ SpaCsrfTokenRequestHandler() {
|
|
|
|
+ this.xor.setCsrfRequestAttributeName(null);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void handle(HttpServletRequest request, HttpServletResponse response, Supplier<CsrfToken> csrfToken) {
|
|
|
|
+ /*
|
|
|
|
+ * Always use XorCsrfTokenRequestAttributeHandler to provide BREACH protection
|
|
|
|
+ * of the CsrfToken when it is rendered in the response body.
|
|
|
|
+ */
|
|
|
|
+ this.xor.handle(request, response, csrfToken);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {
|
|
|
|
+ String headerValue = request.getHeader(csrfToken.getHeaderName());
|
|
|
|
+ /*
|
|
|
|
+ * If the request contains a request header, use
|
|
|
|
+ * CsrfTokenRequestAttributeHandler to resolve the CsrfToken. This applies
|
|
|
|
+ * when a single-page application includes the header value automatically,
|
|
|
|
+ * which was obtained via a cookie containing the raw CsrfToken.
|
|
|
|
+ *
|
|
|
|
+ * In all other cases (e.g. if the request contains a request parameter), use
|
|
|
|
+ * XorCsrfTokenRequestAttributeHandler to resolve the CsrfToken. This applies
|
|
|
|
+ * when a server-side rendered form includes the _csrf request parameter as a
|
|
|
|
+ * hidden input.
|
|
|
|
+ */
|
|
|
|
+ return (StringUtils.hasText(headerValue) ? this.plain : this.xor).resolveCsrfTokenValue(request, csrfToken);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
}
|
|
}
|