# CORS和在Spring Framework裡的應用

# Intro
Cross-Origin Resource Sharing (CORS)
[MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)
[Spec](https://fetch.spec.whatwg.org/#http-cors-protocol)

為了防止XSS Attack，也就是惡意的JS讓使用者發請求到第三方網站，瀏覽器會限制XHR Request只能發到同一個origin。CORS Header讓伺服器端可以設定允許的origin。

# Specifications
## Simple Request & Preflight Request
Simple Request: GET, HEAD, POST with Origin Header
Preflight: OPTIONS with specific headers request before real request. 

## Preflight Request Headers
- `Access-Control-Request-Method` : Specify the coming request method
- `Access-Control-Request-Headers`:  


## CORS respose headers
	- Access-Control-Allow-Origin : **main header concerned**
- `Access-Control-Allow-Credentials`: Is credentials allowed (cookies..etc)
- `Access-Control-Allow-Methods`
- `Access-Control-Allow-Headers`: which HTTP headers can be used during the actual request.
- `Access-Control-Max-Age`
- `Access-Control-Expose-Headers`: response header allows a server to indicate which response headers should be made available to scripts running in the browser

## Fetch Metadata Request Header
`sec-fetch-mode` will be `cors` .

https://developer.mozilla.org/en-US/docs/Glossary/Fetch_metadata_request_header
- 瀏覽器在fetch時會提供metadata，讓Server知道是怎麼樣的請求，比如說是XHR還是直接的request

-   [`Sec-Fetch-Site`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Fetch-Site)
	- cross-site: different domain
	- same-origin: same origin 
	- same-site: same subdomain (e.g. sub.example.com and example.com)
	- none: user triggered request (directly in URL bar, bookmark, other application ..etc)
-   [`Sec-Fetch-Mode`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Fetch-Mode)
	- cors: [[CORS]] request
	- navigate: normal navigation between HTML documents
	- no-cors
	- same-origin
	- websocket
-   [`Sec-Fetch-User`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Fetch-User)
	- Always `?1` if the request is initiated by user.
-   [`Sec-Fetch-Dest`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Fetch-Dest)
	- In what element will be used e.g. audio, image, iframe, document ..etc


# Use curl to send CORS requests
## Normal request
```bash
$ curl -X GET -H "Origin: https://example.com" https://foo.com/aaabbcc
```

## Preflight request
```bash
$ curl -X OPTIONS -H "Access-Control-Request-Method: GET" -H "Origin: https://example.com" https://foo.bar/api/
```
 # Send CORS in browser
Open console tab in development mode and use `fetch(url)`
 

# Spring Boot / Spring Framework

Ref links:
- https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-cors 
- https://spring.io/guides/gs/rest-service-cors/
- https://blog.csdn.net/f641385712/article/details/101036506
- https://www.cnblogs.com/leftthen/p/6378090.html

## Implementation
https://github.com/spring-projects/spring-framework/blob/main/spring-web/src/main/java/org/springframework/web/cors/DefaultCorsProcessor.java


## Support Preflight Requests
OPTIONS request is by default supported
https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-requestmapping-head-options
```
By default, HTTP OPTIONS is handled by setting the `Allow` response header to the list of HTTP methods listed in all `@RequestMapping` methods that have matching URL patterns.

### @CrossOrigin Annotation
https://docs.spring.io/spring-framework/docs/5.3.14/javadoc-api/org/springframework/web/bind/annotation/CrossOrigin.html

Applying to a controller method
Main options: origins, originPatterns(>= 5.3), methods
```

### Java Configuration
Global config in [[Spring Framework]]

```java
package ...;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class CorsConfiguration {
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/api/**").allowedOrigins("https://example.com");
            }
        };
    }
}

```
- https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/CorsRegistry.html#addMapping-java.lang.String-
- https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/CorsRegistration.html#allowedOrigins-java.lang.String...-

note: can't use wildcard in allowedOrigins. Only allowedOriginPatterns.

-https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/CorsRegistry.html

Detailed settings for CORS:
```java
registry.addMapping("url") // -> returns  CorsRegistration
registry.addMapping("url").allowedOrigins(...).allowedHeaders(...).allowCredentials(...)

registry.addMapping("url").allowOriginPatterns() // Spring >= 5.3

```

- https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/CorsRegistration.html

# AWS Cloudfront 
https://aws.amazon.com/premiumsupport/knowledge-center/no-access-control-allow-origin-error/

If CORS related headers are wrongly cached, the behavior would be strange.

Solutions:
1. Add CORS related headers to cache key
2. Use origin request policy
3. Use managed origin request policy
