CORS 全称是 Cross-Origin Resource Sharing,翻译为 跨域资源共享,用于跨域场景发送 HTTP 请求。
两种请求
CORS 可以分为简单请求与非简单请求,非简单请求会多一个预检请求(Preflight request)。 有这个区分是为了兼容表单(form),因为 form 历史就可以发送跨域请求。
最新的 w3c 规范已经没有简单请求这个概念,不过原理一样。
同时满足三个条件就是简单请求,否则是非简单请求
- 请求方法是 HEAD、GET、POST 之一
- 请求头除了浏览器自带的之外,可以手动设置的只有以下几个:
accept
accept-language
content-language
content-type
有其他限制,看第三条range
content-type
的值为以下几种:
text/plain
multipart/form-data
application/x-www-form-urlencoded
简单请求
简单请求,会在请求头中多一个 origin
字段
Origin: http://example.com/
服务器响应中如果有对应的 Access-Control-Allow-Origin
字段,就允许跨域:
<!-- 准确匹配 -->
Access-Control-Allow-Origin: http://example.com/
<!-- 全部匹配 -->
Access-Control-Allow-Origin: *
这里建议的做法是准确匹配,一方面是更安全,另外后面携带 cookie 也会用到。
非简单请求(预检请求)
非简单请求会在正式的传输之前,增加一次预检请求,方法是 OPTIONS
,并携带了三个和跨域请求有关的字段:
Origin: https://example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-Custom-Header, Content-Type
服务器收到请求后,根据这些字段判断是否允许,如果允许,会返回响应头:
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-Custom-Header, Content-Type
Access-Control-Max-Age: 3600
Access-Control-Allow-Methods
表示服务器支持的所有跨域请求方法
Access-Control-Allow-Headers
表示服务器支持的所有头信息字段
Access-Control-Max-Age
表示本次预检请求的有效期,相当于缓存,单位是秒。再有效期内如果是 相同 URL 发出的跨域请求,不会再次发出新一条预检请求。如果是在生产环境使用 CORS,设置缓存很重要,可以减少请求数。Chromium 从 76 版本开始,缓存上限是 2 小时,即 7200s。
携带 cookie
CORS 默认不会携带 cookie,如果携带 cookie,Access-Control-Allow-Origin
需要精确匹配,并且需要额外设置:
请求设置
// XHR 设置
const xhr = new XMLHttpRequest()
xhr.open('GET', 'http://example.com/', true)
xhr.withCredentials = true
xhr.send(null)
// fetch 设置
fetch('https://example.com', {
credentials: 'include',
})
响应设置:Access-Control-Allow-Origin
精确匹配,Access-Control-Allow-Credentials
设置为 true
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Credentials: true