CORS Support#
Cross-Origin Resource Sharing (CORS) is a security concept that allows restricting the resources implemented in web browsers. It prevents the JavaScript code producing or consuming the requests against different origin.
Cross-Origin Requests#
Cross-origin requests, in short, are HTTP requests where the origin and the target of the request are different.
To manage cross-origin requests, the server needs to enable a particular mechanism known as CORS, or Cross-Origin Resource Sharing.
The first step in CORS is an OPTIONS
request to determine whether the target of the request supports it. This is called a pre-flight request.
The server can then respond to the pre-flight request with a collection of headers:
Access-Control-Allow-Origin
: Defines which origins may have access to the resource. A*
represents any originAccess-Control-Allow-Methods
: Indicates the allowed HTTP methods for cross-origin requestsAccess-Control-Allow-Headers
: Indicates the allowed request headers for cross-origin requestsAccess-Control-Max-Age
: Defines the expiration time of the result of the cached preflight request
If configured incorrectly, this pre-flight request will always fail with a 401.
Enable CORS in Controller Method#
@RequestMapping(value = "/products")
@CrossOrigin(origins = "http://localhost:8080")
public ResponseEntity<Object> getProduct() {
return null;
}
The @CrossOrigin
annotation makes sure that our APIs are accessible only from the origin mentioned in its argument.
属性 | 含义 |
---|---|
value |
指定所支持域的集合, 表示所有域都支持,默认值为 。这些值对应于 HTTP 请求头中的 Access-Control-Allow-Origin |
origins |
@AliasFor(“value”) ,与 value 属性一样 |
allowedHeaders |
允许请求头中的 headers,在预检请求 Access-Control-Allow-Headers 响应头中展示 |
exposedHeaders |
响应头中允许访问的 headers,在实际请求的 Access-Control-Expose-Headers 响应头中展示 |
methods |
支持的 HTTP 请求方法列表,默认和 Controller 中的方法上标注的一致。 |
allowCredentials |
表示浏览器在跨域请求中是否携带凭证,比如 cookies。在预检请求的 Access-Control-Allow-Credentials 响应头中展示 |
maxAge |
预检请求响应的最大缓存时间,单位为秒。在预检请求的 Access-Control-Max-Age 响应头中展示 |
Global CORS Configuration#
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/products").allowedOrigins("http://localhost:9000");
}
};
}
Making a Pre-flight Request#
curl -v -H "Access-Control-Request-Method: GET" -H "Origin: http://localhost:4200" -X OPTIONS http://localhost:8071/products-cors
...
< HTTP/1.1 401
...
< WWW-Authenticate: Basic realm="Realm"
...
< Vary: Origin
< Vary: Access-Control-Request-Method
< Vary: Access-Control-Request-Headers
< Access-Control-Allow-Origin: http://localhost:4200
< Access-Control-Allow-Methods: POST
< Access-Control-Allow-Credentials: true
< Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
...
curl -v -H "Access-Control-Request-Method: GET" -H "Origin: http://localhost:8072" -X OPTIONS http://localhost:8071/products-cors
curl -v -H "Access-Control-Request-Method: GET" -H "Origin: http://localhost:4200" -X OPTIONS http://localhost:8072/user-cors
The Solution#
We haven’t explicitly excluded the preflight requests from authorization in our Spring Security configuration. Remember that Spring Security secures all endpoints by default.
As a result, our API expects an authorization token in the OPTIONS request as well.
Spring provides an out of the box solution to exclude OPTIONS requests from authorization checks:
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors();
}
}
The cors()
method will add the Spring-provided CorsFilter
to the application context which in turn bypasses the authorization checks for OPTIONS requests.
How does it work?#
CORS requests (including preflight ones with an OPTIONS method) are automatically dispatched to the various HandlerMapping
s registered. They handle CORS preflight requests and intercept CORS simple and actual requests thanks to a CorsProcessor implementation (DefaultCorsProcessor by default) in order to add the relevant CORS response headers (like Access-Control-Allow-Origin
). CorsConfiguration allows you to specify how the CORS requests should be processed: allowed origins, headers, methods, etc. It can be provided in various ways:
AbstractHandlerMapping#setCorsConfiguration() allows to specify a
Map
with several CorsConfiguration mapped on path patterns like/api/**
Subclasses can provide their own
CorsConfiguration
by overridingAbstractHandlerMapping#getCorsConfiguration(Object, HttpServletRequest)
methodHandlers can implement
CorsConfigurationSource
interface (likeResourceHttpRequestHandler
now does) in order to provide aCorsConfiguration
for each request.
Filter based CORS support#
In that case, instead of using @CrossOrigin
or WebMvcConfigurer#addCorsMappings(CorsRegistry)
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("http://localhost:4200");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
Tips#
URI 例子及它们的组成部分:
权限 路径
┌───────────────┴───────────────┐┌───┴────┐
abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1
└┬┘ └───────┬───────┘ └────┬────┘ └┬┘ └─────────┬─────────┘ └──┬──┘
协议 用户信息 主机名 端口 查询参数 片段
浏览器的同源策略#
只要 协议,主机名,端口 这三项组成部分中有一项不同,就可以认为是不同的域,不同的域之间互相访问资源,就被称之为跨域
同源策略会限制以下几种行为:
Cookie、LocalStorage 和 IndexDB 无法读取
DOM 和 JS 对象无法获得
AJAX 请求不能发送,被浏览器拦截了
跨域解决方案#
JSONP 跨域
跨域资源共享(CORS)
Nginx 反向代理
Node.js 中间件代理
document.domain + iframe
location.hash + iframe 跨域
window.name + iframe 跨域
postMessage 跨域
WebSocket 协议跨域