Add the cors-csrf module
This commit is contained in:
99
cors-csrf/docs/02-who-resolves-the-source.md
Normal file
99
cors-csrf/docs/02-who-resolves-the-source.md
Normal file
@@ -0,0 +1,99 @@
|
||||
# 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)*
|
||||
Reference in New Issue
Block a user