The full picture
- The decision rule: 'could a cross-site request arrive pre-authenticated?' Cookies/Basic → yes → CSRF protection on. Bearer-token-only → no →
csrf.disable()is correct, not lazy. Cookie-based SPA → CSRF on (cookie-to-header pattern) plusSameSite=Lax/Strictas modern layered defense. - CORS is not access control: it protects browser users, not your API — curl ignores it entirely. Auth still does the real protection; CORS just stops malicious-origin JS from riding a user's browser session to read responses.
- CORS in Spring: configure in the security chain (
http.cors(…)+ aCorsConfigurationSource) so preflights (OPTIONS) pass before auth filters reject them — the 'CORS works locally, 401s on preflight in prod' classic. - Never
allowedOrigins("*")withallowCredentials(true)— the spec forbids it and Spring throws; wildcard origins with credentials would be cookie-theft-as-a-service. Use explicit origins or patterns.
http
.cors(c -> c.configurationSource(req -> {
var cfg = new CorsConfiguration();
cfg.setAllowedOrigins(List.of("https://app.acme.com")); // explicit
cfg.setAllowedMethods(List.of("GET","POST","PUT","DELETE"));
cfg.setAllowedHeaders(List.of("Content-Type","X-XSRF-TOKEN"));
cfg.setAllowCredentials(true);
return cfg;
}))
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler()));
// SPA reads XSRF-TOKEN cookie -> sends X-XSRF-TOKEN header; attacker's site
// can trigger the request but cannot READ the cookie to forge the header.