100 lines
4.1 KiB
Markdown
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)*
|