1
0
Files
spring-security-demo/cors-csrf/docs/02-who-resolves-the-source.md
2026-08-28 10:16:21 +05:30

100 lines
4.1 KiB
Markdown

# 2. Who resolves the `CorsConfigurationSource`
*Prev: [1. Two layers, one word](01-two-layers.md) · Next: [3. The three identical 403s](03-three-identical-403s.md)*
There are two lookups involved in getting a CORS configuration into the filter chain, and they
disagree about what they are looking for. One searches by **type**. The other searches by
**name**.
## Lookup 1: should the CORS configurer run at all?
`HttpSecurityConfiguration.applyCorsIfAvailable(HttpSecurity)`, disassembled from
`spring-security-config` 7.1.1:
```
4: ldc // class org/springframework/web/cors/UrlBasedCorsConfigurationSource
6: invokeinterface // ApplicationContext.getBeanNamesForType:(Ljava/lang/Class;)[Ljava/lang/String;
11: arraylength
12: ifle 23
19: invokevirtual // HttpSecurity.cors:(Customizer)HttpSecurity
23: return
```
By type, and the test is `ifle` — "branch if less than or equal to zero". One bean is
enough. So is five.
> The reference documentation says: *"If you have more than one `CorsConfigurationSource` bean,
> Spring Security won't automatically configure CORS support for you, because it cannot decide
> which one to use."* That is not what 7.1.1 does.
> [`docs/output/06-two-sources.txt`](output/06-two-sources.txt) has two such beans, CORS
> configured, and one of them serving traffic.
## Lookup 2: which source does the configurer use?
`CorsConfigurer.getCorsConfigurationSource(ApplicationContext)`:
```
7: ldc // String corsConfigurationSource
9: invokeinterface // ApplicationContext.containsBeanDefinition:(Ljava/lang/String;)Z
20: ldc // String corsConfigurationSource
24: invokeinterface // ApplicationContext.getBean:(String,Class)Object
34: invokestatic // MvcCorsFilter.getMvcCorsConfigurationSource:(ApplicationContext)CorsConfigurationSource
```
By name. The literal string `corsConfigurationSource`. If no bean definition carries that name,
it falls through to Spring MVC's registrations. (There is a similar name check first, for a
`CorsFilter` bean named `corsFilter`.)
## The gap between them
Name a `UrlBasedCorsConfigurationSource` bean anything other than `corsConfigurationSource` and
you land between the two lookups: CORS is switched **on** by the type lookup, and the
configuration you wrote is **ignored** by the name lookup.
That is the `misnamed` profile, and it produces the worst diagnostic in this whole subject:
```
HTTP/1.1 200
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
...
```
Two hundred. No `Access-Control-Allow-Origin`. The browser blocks the request and reports a CORS
error; your access log shows a successful `OPTIONS`; nothing anywhere is red.
Why 200 rather than the 401 from chapter 1? Because `CorsFilter` is now in the chain, and its
`if (!isValid || isPreFlightRequest(request)) return;` fires on the second clause. The request
never reaches `AuthorizationFilter`. The only trace is one DEBUG line:
```
o.s.web.cors.DefaultCorsProcessor : Skip: no CORS configuration has been provided
```
Full transcript: [`docs/output/05-misnamed-bean.txt`](output/05-misnamed-bean.txt).
## What is in the context that you did not put there
`/diag/cors-sources` on a stock Boot web application:
```json
{ "corsConfigurationSourceBeans": { "mvcHandlerMappingIntrospector": "HandlerMappingIntrospector" } }
```
`HandlerMappingIntrospector` implements `CorsConfigurationSource`. It is always there, it is not
a `UrlBasedCorsConfigurationSource`, and it is the object the MVC fallback returns. That is why
the fallback path never throws in a normal application — and why the failure is silent
rather than loud.
## Rules that follow
- Name the bean `corsConfigurationSource`. Exactly that.
- If you want several, pass them per chain with `.cors(c -> c.configurationSource(..))`, which
bypasses both lookups.
- `NoSuchBeanDefinitionException: Failed to find a bean that implements
\`CorsConfigurationSource\`` names three fixes and does not mention the fourth one, which is
usually the right one: rename your bean.
---
*Prev: [1. Two layers, one word](01-two-layers.md) · Next: [3. The three identical 403s](03-three-identical-403s.md)*