@@ -788,55 +788,43 @@ public class SecurityConfig {
788
788
.csrf((csrf) -> csrf
789
789
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) // <1>
790
790
.csrfTokenRequestHandler(new SpaCsrfTokenRequestHandler()) // <2>
791
- )
792
- .addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class); // <3>
791
+ );
793
792
return http.build();
794
793
}
795
794
}
796
795
797
- final class SpaCsrfTokenRequestHandler extends CsrfTokenRequestAttributeHandler {
798
- private final CsrfTokenRequestHandler delegate = new XorCsrfTokenRequestAttributeHandler();
796
+ final class SpaCsrfTokenRequestHandler implements CsrfTokenRequestHandler {
797
+ private final CsrfTokenRequestHandler plain = new CsrfTokenRequestAttributeHandler();
798
+ private final CsrfTokenRequestHandler xor = new XorCsrfTokenRequestAttributeHandler();
799
799
800
800
@Override
801
801
public void handle(HttpServletRequest request, HttpServletResponse response, Supplier<CsrfToken> csrfToken) {
802
802
/*
803
803
* Always use XorCsrfTokenRequestAttributeHandler to provide BREACH protection of
804
804
* the CsrfToken when it is rendered in the response body.
805
805
*/
806
- this.delegate.handle(request, response, csrfToken);
806
+ this.xor.handle(request, response, csrfToken);
807
+ /*
808
+ * Render the token value to a cookie by causing the deferred token to be loaded.
809
+ */
810
+ csrfToken.get();
807
811
}
808
812
809
813
@Override
810
814
public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {
815
+ String headerValue = request.getHeader(csrfToken.getHeaderName());
811
816
/*
812
817
* If the request contains a request header, use CsrfTokenRequestAttributeHandler
813
818
* to resolve the CsrfToken. This applies when a single-page application includes
814
819
* the header value automatically, which was obtained via a cookie containing the
815
820
* raw CsrfToken.
816
- */
817
- if (StringUtils.hasText(request.getHeader(csrfToken.getHeaderName()))) {
818
- return super.resolveCsrfTokenValue(request, csrfToken);
819
- }
820
- /*
821
+ *
821
822
* In all other cases (e.g. if the request contains a request parameter), use
822
823
* XorCsrfTokenRequestAttributeHandler to resolve the CsrfToken. This applies
823
824
* when a server-side rendered form includes the _csrf request parameter as a
824
825
* hidden input.
825
826
*/
826
- return this.delegate.resolveCsrfTokenValue(request, csrfToken);
827
- }
828
- }
829
-
830
- final class CsrfCookieFilter extends OncePerRequestFilter {
831
-
832
- @Override
833
- protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
834
- throws ServletException, IOException {
835
- CsrfToken csrfToken = (CsrfToken) request.getAttribute("_csrf");
836
- // Render the token value to a cookie by causing the deferred token to be loaded
837
- csrfToken.getToken();
838
-
839
- filterChain.doFilter(request, response);
827
+ return (StringUtils.hasText(headerValue) ? this.plain : this.xor).resolveCsrfTokenValue(request, csrfToken);
840
828
}
841
829
}
842
830
----
@@ -856,55 +844,49 @@ class SecurityConfig {
856
844
http {
857
845
// ...
858
846
csrf {
859
- csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse() // <1>
860
- csrfTokenRequestHandler = SpaCsrfTokenRequestHandler() // <2>
847
+ csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse() // <1>
848
+ csrfTokenRequestHandler = SpaCsrfTokenRequestHandler() // <2>
861
849
}
862
850
}
863
- http.addFilterAfter(CsrfCookieFilter(), BasicAuthenticationFilter::class.java) // <3>
864
851
return http.build()
865
852
}
866
853
}
867
854
868
- class SpaCsrfTokenRequestHandler : CsrfTokenRequestAttributeHandler() {
869
- private val delegate: CsrfTokenRequestHandler = XorCsrfTokenRequestAttributeHandler()
855
+ class SpaCsrfTokenRequestHandler : CsrfTokenRequestHandler {
856
+ private val plain: CsrfTokenRequestHandler = CsrfTokenRequestAttributeHandler()
857
+ private val xor: CsrfTokenRequestHandler = XorCsrfTokenRequestAttributeHandler()
870
858
871
859
override fun handle(request: HttpServletRequest, response: HttpServletResponse, csrfToken: Supplier<CsrfToken>) {
872
860
/*
873
861
* Always use XorCsrfTokenRequestAttributeHandler to provide BREACH protection of
874
862
* the CsrfToken when it is rendered in the response body.
875
863
*/
876
- delegate.handle(request, response, csrfToken)
864
+ xor.handle(request, response, csrfToken)
865
+ /*
866
+ * Render the token value to a cookie by causing the deferred token to be loaded.
867
+ */
868
+ csrfToken.get()
877
869
}
878
870
879
871
override fun resolveCsrfTokenValue(request: HttpServletRequest, csrfToken: CsrfToken): String? {
872
+ val headerValue = request.getHeader(csrfToken.headerName)
880
873
/*
881
874
* If the request contains a request header, use CsrfTokenRequestAttributeHandler
882
875
* to resolve the CsrfToken. This applies when a single-page application includes
883
876
* the header value automatically, which was obtained via a cookie containing the
884
877
* raw CsrfToken.
885
878
*/
886
- return if (StringUtils.hasText(request.getHeader(csrfToken.headerName) )) {
887
- super.resolveCsrfTokenValue(request, csrfToken)
879
+ return if (StringUtils.hasText(headerValue )) {
880
+ plain
888
881
} else {
889
882
/*
890
883
* In all other cases (e.g. if the request contains a request parameter), use
891
884
* XorCsrfTokenRequestAttributeHandler to resolve the CsrfToken. This applies
892
885
* when a server-side rendered form includes the _csrf request parameter as a
893
886
* hidden input.
894
887
*/
895
- delegate.resolveCsrfTokenValue(request, csrfToken)
896
- }
897
- }
898
- }
899
-
900
- class CsrfCookieFilter : OncePerRequestFilter() {
901
-
902
- @Throws(ServletException::class, IOException::class)
903
- override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain) {
904
- val csrfToken = request.getAttribute("_csrf") as CsrfToken
905
- // Render the token value to a cookie by causing the deferred token to be loaded
906
- csrfToken.token
907
- filterChain.doFilter(request, response)
888
+ xor
889
+ }.resolveCsrfTokenValue(request, csrfToken)
908
890
}
909
891
}
910
892
----
@@ -916,23 +898,20 @@ XML::
916
898
<http>
917
899
<!-- ... -->
918
900
<csrf
919
- token-repository-ref="tokenRepository" <1>
920
- request-handler-ref="requestHandler"/> <2>
921
- <custom-filter ref="csrfCookieFilter" after="BASIC_AUTH_FILTER"/> <3>
901
+ token-repository-ref="tokenRepository" <1>
902
+ request-handler-ref="requestHandler"/> <2>
922
903
</http>
923
904
<b:bean id="tokenRepository"
924
905
class="org.springframework.security.web.csrf.CookieCsrfTokenRepository"
925
906
p:cookieHttpOnly="false"/>
926
907
<b:bean id="requestHandler"
927
908
class="example.SpaCsrfTokenRequestHandler"/>
928
- <b:bean id="csrfCookieFilter"
929
- class="example.CsrfCookieFilter"/>
930
909
----
931
910
======
932
911
933
912
<1> Configure `CookieCsrfTokenRepository` with `HttpOnly` set to `false` so the cookie can be read by the JavaScript application.
934
913
<2> Configure a custom `CsrfTokenRequestHandler` that resolves the CSRF token based on whether it is an HTTP request header (`X-XSRF-TOKEN`) or request parameter (`_csrf`).
935
- <3> Configure a custom `Filter` to load the `CsrfToken` on every request, which will return a new cookie if needed.
914
+ This implementation also causes the deferred `CsrfToken` to be loaded on every request, which will return a new cookie if needed.
936
915
937
916
[[csrf-integration-javascript-mpa]]
938
917
==== Multi-Page Applications
0 commit comments