CORS
2018-12-11
什么是跨域请求 ?
当一个资源从另外一个与该资源有不同域名(Domain),协议(protocal),端口(port)的服务器请求资源时,所发出的请求即为跨域请求。
由于某些安全因素,浏览器限制跨域请求,XMLHttpRequest
和 Fetch
都遵循同源策略。
哪些请求涉及CORS
- 刚刚提到的通过
XMLHttpRequest
或Fetch
进行的跨域请求 - 请求涉及 Web Fonts
- 通过 canvas
drawImage()
进行构图的图片或者视频
什么是跨域资源共享 ?
CORS is the mechanism that use additional HTTP headers to tell a browser to let a web application running at one origin(domani) have permission to access selected resources from a server at a different.
跨域资源共享是一种机制,通过设置额外的HTTP头部来授权不同域名的网站或者Web app通过HTTP请求来本网站的选定内容。
也就是说通过CORS, 我们可以在服务器端设置谁可以访问我们的资源。
而且对于一些有副作用的请求,CORS要求浏览器进行”预检验“。浏览器首先向服务器发送OPTIONS
请求,浏览器获得服务器允许后会真正的请求。
跨域请求失败后, JavaScript代码是无法直接了解到错误信息的, 只有在console才能看到报错信息。
CORS 处理的三种请求
简单请求
有些请求不会触发CORS预检验,这些所谓的简单请求要满足以下所有条件:
- 使用GET, HEAD, POST方法
- 除了浏览器自动设置的请求头部,和下列请求头部之外又设置了自定义头部的请求。
Accept
Accept-Language
Content-Language
Content-Type
DPR
DownLink
Save-Data
Viewport-Width
Width
Content-Type
为以下值的请求:applicaton/x-www-form-urlencoded
multipart/form-data
text/palin
XMLHttpRequestUpload
对象上没有注册时间监听的请求没有使用任何
ReadableStream
对象的请求一个简单请求的处理过程
对应的HTTP请求与响应
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
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
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
Origin: http://foo.example
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2.0.61
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml
[XML Data]
请求中Origin
表明了该请求由哪个域名发出,而在服务器的响应中设定了Access-Control-Allow-Origin
头部, 其值为*
表示请求的Origin
为任何值都可以访问,如果设置Access-Control-Allow-Origin
的值为http://foo.example
则只有来自于http://foo.example
的请求可以访问对应资源。
预检验请求
由于预检验请求可能会影响用户数据,预检验请求需要先向服务器发送OPTIONS
请求来征求服务器同意。
满足下列任意条件之一的请求都需要预检验:
- 使用如下方法:
PUT
DELETE
CONNECT
OPTIONS
TRACE
PATCH
- 除了浏览器自动设置的头部,和以下头部外,包括其他自定义头部:
Accept
Accept-Language
Content-Language
Content-Type
DPR
DownLink
Save-Data
Viewport-Width
Width
Content-Type
设置为除了以下值的其他值:applicaton/x-www-form-urlencoded
multipart/form-data
text/palin
XMLHttpRequestUpload
对象上注册时间监听的请求- 使用任何
ReadableStream
对象的请求
发送一个预检验请求
1 | const invocation = new XMLHttpRequest(); |
该请求通过POST方法发送XML格式数据,并设置了自定义头部X-PINGOTHER:pingpong
,所有需要预检验。
这个预检验流程
1 | OPTIONS /resources/post-here/ HTTP/1.1 |
Access-COntrol-Request-Method:POST
告知服务器真实请求的请求方法,而Access-Control-Request-Headers: X-PINGOTHER, Content-Type
则告知真实请求会发送的自定义头部。
1 | Access-Control-Allow-Origin: http://foo.example |
服务器通过以上响应头部告知浏览器服务器所接受的方法,域名,头部和缓存时间。
发送凭证的请求
默认情况下,跨域的XMLHttpRequest
和Fetch
调用不会发送凭证。需要特别设置flag才能发送。
跨域发送Cookies
1 | const invocation = new XMLHttpRequest(); |
设置withCredentials
值为true后可以发送Cookies, 由于是简单的GET
请求,所以不会进行预检验。但是如果响应中不包含Aceess-Control-Allow-Credentials:ture
的话浏览器不会显示响应内容。
1 | GET /resources/access-control-with-credentials/ HTTP/1.1 |
当服务器响应带有凭证的跨域请求的时候, 需要在响应的Access-Control-Allow-Origin
中指定域名,如果只设置为*
通配符的话,请求会失败。