跨域资源共享 (CORS)(或通俗地译为跨域资源共享)是一种基于 HTTP 头的机制,该机制通过允许服务器标示除了它自己以外的其它 origin
(域,协议和端口),使得浏览器允许这些 origin
访问加载自己的资源。如果浏览器符合服务器的要求,那么它将可以访问此服务器host
内的内容
跨域HTTP 请求的一个例子:运行在 https://a.com
的 JavaScript 代码使用XMLHttpRequest
来发起一个到 https://b.com/data.json
的请求。
但是浏览器会限制JS跨域HTTP的请求,跨域资源共享(CORS)机制允许 Web 应用服务器进行跨源访问控制,从而使跨源数据传输得以安全进行
通常由axios(AJAX)
即内部的XMLHttpRequest
与Fetch APIs
发起的跨域HTTP请求需要用到CORS,还有一些Web字体。图像等
CORS新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。
另外,有时候,非GET
请求需要首先使用OPTIONS
方法发起一个预检请求
,从而知道服务器是否允许我们跨域请求
在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies
和 HTTP 认证
相关数据)。
不会触发CORS预检请求
的请求称为简单请求
,要求满足下列所有条件:
①使用GET
,POST
,HEAD
②处于下列首部字段内
Accept
Accept-Language
Content-Language
Content-Type(需要注意额外的限制)
③Content-Type 的值仅限于下列三者之一:
text/plain
multipart/form-data
application/x-www-form-urlencoded
④请求中的任意 XMLHttpRequest
对象均没有注册任何事件监听器;XMLHttpRequest
对象可以使用 XMLHttpRequest.upload
属性访问以及请求中没有使用 ReadableStream
对象
比如说,假如站点 https://foo.example
的网页应用想要访问 https://bar.other
的资源。foo.example
的网页中可能包含类似于下面的 JavaScript 代码:
const xhr = new XMLHttpRequest();
const url = 'https://bar.other/resources/public-data/';
xhr.open('GET', url);
xhr.onreadystatechange = someHandler;
xhr.send();
那么客户端和服务器之间使用 CORS 首部字段来处理权限:
请求头:
GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: https://foo.example
请求首部字段 Origin
表明该请求来源于 http://foo.example
响应头:
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml
[XML Data]
Access-Control-Allow-Origin: *
说明该网站内的该资源可以被任意网址访问
Access-Control-Allow-Origin: https://foo.example
说明该网站内的该资源只能允许来自https://foo.example
访问
请求头
OPTIONS /doc HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: https://foo.example
Access-Control-Request-Method: POST
上面表示实际请求是POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
上面表示将会携带自定义的2个头
响应头
HTTP/1.1 204 No Content
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2
Access-Control-Allow-Origin: https://foo.example
上面表示允许访问的origin
Access-Control-Allow-Methods: POST, GET, OPTIONS
上面表示允许的几个发送信息方式
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
上面表示允许的几个携带自定义的头
Access-Control-Max-Age: 86400
上面指定了 preflight 请求的结果能够被缓存几秒(s)
Vary: Accept-Encoding, Origin
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
预检请求完成之后,发送实际请求:
POST /doc HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Referer: https://foo.example/examples/preflightInvocation.html
Content-Length: 55
Origin: https://foo.example
Pragma: no-cache
Cache-Control: no-cache
Arun
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2
Access-Control-Allow-Origin: https://foo.example
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain
[Some XML payload]
其中,xhr
对象的withCredentials
方法=true
时会向服务器发送cookie
但是,如果服务器端的响应中未携带 Access-Control-Allow-Credentials: true
,浏览器将不会把响应内容返回给请求的发送者。