CORS和在Spring Framework裡的應用
Intro
Cross-Origin Resource Sharing (CORS) MDN Spec
為了防止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 methodAccess-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-MethodsAccess-Control-Allow-Headers: which HTTP headers can be used during the actual request.Access-Control-Max-AgeAccess-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
-
- 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- cors: [[CORS]] request
- navigate: normal navigation between HTML documents
- no-cors
- same-origin
- websocket
Sec-Fetch-User- Always
?1if the request is initiated by user.
- Always
Sec-Fetch-Dest- In what element will be used e.g. audio, image, iframe, document ..etc
Use curl to send CORS requests
Normal request
$ curl -X GET -H "Origin: https://example.com" https://foo.com/aaabbcc
Preflight request
$ 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]]
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:
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:
- Add CORS related headers to cache key
- Use origin request policy
- Use managed origin request policy