1
0
Files
spring-security-demo/cors-csrf/docs/04-preflight-handlers.md
2026-08-28 10:16:21 +05:30

3.5 KiB

4. PreFlightRequestHandler, and the wildcard that is not allowed

Prev: 3. The three identical 403s · Next: 5. The /error dispatch

preFlightRequestHandler

CorsConfigurer in 7.1.1 has a second setter beside configurationSource:

public CorsConfigurer<H> preFlightRequestHandler(PreFlightRequestHandler handler);

When one is selected, Spring Security registers Spring Framework's PreFlightRequestFilter before CorsFilter in the chain — addFilterBefore(.., CorsFilter.class), which lands it at 999. It is for applications that answer preflights from their own routing rather than from a CorsConfiguration, and it is the hook WebFlux-style functional routing and gateway-shaped applications want.

The handler is picked up either from the preFlightRequestHandler(..) call or from a PreFlightRequestHandler bean, and only when no CorsConfigurationSource or CorsFilter was chosen for that chain. Configuring both raises, at startup:

java.lang.IllegalStateException: Cannot configure both a CorsConfigurationSource and a
PreFlightRequestHandler on CorsConfigurer

That string is in CorsConfigurer.configure's constant pool; it is a hard failure, not a warning.

allowedOrigins("*") with allowCredentials(true)

The Fetch standard forbids answering a credentialed request with Access-Control-Allow-Origin: *. Spring enforces it — but not where you would expect.

The configuration builds. The context starts. The check happens on the first request, inside CorsConfiguration.validateAllowCredentials, reached from checkOrigin:

java.lang.IllegalArgumentException: When allowCredentials is true, allowedOrigins cannot contain
the special value "*" since that cannot be set on the "Access-Control-Allow-Origin" response
header. To allow credentials to a set of origins, list them explicitly or consider using
"allowedOriginPatterns" instead.
	at org.springframework.web.cors.CorsConfiguration.validateAllowCredentials(CorsConfiguration.java:552)
	at org.springframework.web.cors.CorsConfiguration.checkOrigin(CorsConfiguration.java:678)
	at org.springframework.web.cors.DefaultCorsProcessor.checkOrigin(DefaultCorsProcessor.java:193)

And here is the part worth knowing: the client does not get a 500. It gets a 401. docs/output/07-wildcard-credentials.txt shows a request with entirely correct Basic credentials answered 401 WWW-Authenticate: Basic. Chapter 5 is why.

The fix is setAllowedOriginPatterns(..), which echoes the request's own origin back instead of a literal asterisk, and is therefore legal with credentials:

configuration.setAllowedOriginPatterns(List.of("https://*.example.com"));
configuration.setAllowCredentials(true);

allowedHeaders("*") and allowedMethods("*") are unaffected — the prohibition is specific to the origin, because that is the one that gets reflected into a header the browser uses to decide whether the caller may read a credentialed response.

Private Network Access

DefaultCorsProcessor in Spring Framework 7.0.9 also handles Access-Control-Request-Private-Network / Access-Control-Allow-Private-Network — both strings are in the class. If a public-origin SPA calls something on a private address, that is the header pair to look for, and CorsConfiguration.setAllowPrivateNetwork(true) is the switch.


Prev: 3. The three identical 403s · Next: 5. The /error dispatch