CORS 跨域方案
一、认识
跨源资源共享 Cross-origin resource sharing
(CORS
,或通俗地译为跨域资源共享)是一种基于 HTTP
头的机制,该机制通过允许服务器标示除了它自己以外的其他源(域、协议或端口),使得浏览器允许这些源访问加载自己的资源。
跨源资源共享还通过一种机制来检查服务器是否会允许要发送的真实请求,该机制通过浏览器发起一个到服务器托管的跨源资源的预检请求。在预检中,浏览器发送的头中标示有 HTTP
方法和真实请求中会用到的头。
二、工作
2.1 简单请求
Cors
规范定义简单请求的原则是: 请求不是以更新(添加、修改和删除)资源为目的,服务端对请求的处理不会导致自身维护资源的改变。对于一个请求,必须同时符合如下要求才被划为简单请求规则:
-
Http Method
只能为其一GET
POST
HEAD
-
请求头只能在如下范围
-
Accept
-
Accept-Language
-
Content-Language
-
Content-Type
,其中它的值必须如下其一:-
text/plain
-
multipart/form-data
-
application/x-www-form-urlencoded
-
-
服务端需要设置的响应头
-
Access-Control-Allow-Origin
(必含) -
Access-Control-Allow-Credentials
(可选): 如果简单请求有传Cookie
,那么需要设置Cookie
2.2 预检请求
非简单请求: 简单请求之外的都为非简单请求,也称为复杂请求。比如说需要发送 PUT
、DELETE
等HTTP
动作,或者发送Content-Type:application/json
的内容或者发送自定义请求头。
非简单请求可能对服务端资源改变,因此 Cors
规定浏览器在发出此类请求之前必须有一个**预检(Preflight
)**机制,这也就是我们熟悉的 **OPTIONS
**请求。
Preflight
预检机制
顾名思义,它表示在浏览器发出真正请求之前,先发送一个预检请求,这个在Http
里就是 OPTIONS
请求方式。这个请求很特殊,它不包含主体(无请求参数、请求体等),主要就是将一些凭证、授权相关的辅助信息放在请求头里交给服务器去做决策。
Preflight
预检机制请求头
-
Origin
-
Access-Control-Request-Method
-
Access-Control-Request-Headers
: 由content-type
、自定义请求头
组成
服务端需要设置的响应头
-
Content-Type
-
Access-Control-Allow-Methods
-
Access-Control-Allow-Headers
-
Access-Control-Allow-Origin
(必含) -
Access-Control-Allow-Credentials
(可选): 如果简单请求有传Cookie
,那么需要设置Cookie
Preflight
预检机制预检过程
-
服务端收到
Preflight OPTIONS
请求,允许预检请求通过,返回个200
,并且响应头中包含服务端设置的响应头信息。如果不处理**Preflight OPTIONS
请求**,那么浏览器直接会显示404
-
复杂请求先通过自己的
Origin
匹配预检响应中的 **Access-Control-Allow-Origin
**的值,若不匹配就结束请求,若匹配就继续下一步验证 -
拿到预检响应中的
Access-Control-Allow-Methods
头。若此头不存在,则进行下一步,若存在则校请求头Access-Control-Request-Method
的值是否在此列表中,在其内继续下一步,否则失败 -
拿到预检响应中的**
Access-Control-Request-Headers
头。同 请求头 中的**
Access-Control-Allow-Headers` 值进行比较,全部包含在内则匹配成功,否则失败 -
以上全部匹配成功,就代表预检成功,可以开始发送正式请求了。值得一提的事,
Access-Control-Max-Age
控制预检结果的浏览器缓存,若缓存还生效的话,是不用单独再发送OPTIONS
请求的,匹配成功直接发送目标真实即可。
Access-Control-Max-Age
使用细节
-
若浏览器禁用了缓存,也就是勾选了
Disable cache
,那么此属性无效。也就说每次都还得发送OPTIONS请求 -
判断此缓存结果的因素有两个:
-
必须是同一
URL
(也就是Origin
相同才会去找对应的缓存) -
header
变化了,也会重新去发OPTIONS
请求(当然若去掉一些header
编程简单请求了,就另当别论喽)
-
三、来源
表示请求来源的有三个字段,分别是 Host
、Origin
、**Referer
**三个字段
3.1 Host
Host
表示请求地址,为客户端将要访问的远程主机。浏览器在发送Http请求时会带有此Header
格式: 域名+端口号
3.2 Origin
Origin
表示当前客户端页面地址。用于Cors请求和同域POST请求
格式: 协议+域名+端口
携带场景:
- 同域请求只有post请求才会携带Origin
- 跨域请求都会携带Origin,无论是get请求还是post请求
3.3 Referer
Referer
表示当前客户端页面地址。服务端一般使用Referer首部识别访问来源,可能会以此进行防盗链、统计分析、日志记录以及缓存优化等
格式: 协议+域名+端口号+路径+参数
携带场景: 除了以下不会携带的场景,其余场景都会携带
不会携带的场景:
- 来源页面协议为File或者Data URI(如页面从本地打开的)
- 来源页面是Https,而目标URL是http
- 浏览器地址栏直接输入网址访问,或者通过浏览器的书签直接访问
- 使用JS的location.href跳转
四、请求头
用于发起跨源请求的标头字段。请注意,这些标头字段无须手动设置。当开发者使用 XMLHttpRequest
对象发起跨源请求时,它们已经被设置就绪。
4.1 Origin
Origin
标头字段表明预检请求或实际跨源请求的源站。origin
参数的值为源站 URL
。它不包含任何路径信息,只是服务器名称。注意,在所有访问控制请求中,Origin
标头字段总是被发送。
4.2 Access-Control-Request-Method
Access-Control-Request-Method
标头字段用于预检请求。其作用是,将实际请求所使用的 HTTP
方法告诉服务器。
4.3 Access-Control-Request-Headers
Access-Control-Request-Headers
标头字段用于预检请求。其作用是,将实际请求所携带的标头字段(通过 setRequestHeader()
等设置的)告诉服务器。这个浏览器端标头将由互补的服务器端标头 Access-Control-Allow-Headers
回答。
五、响应头
5.1 Context-Type
5.2 Access-Control-Allow-Origin
Access-Control-Allow-Origin
告诉浏览器允许该源访问资源。对于不需要携带身份凭证的请求,服务器可以指定该字段的值为通配符*
,表示允许来自任意源的请求。
语法
Access-Control-Allow-Origin: https://mozilla.org
Vary: Origin
5.3 Access-Control-Expose-Headers
在跨源访问时,XMLHttpRequest
对象的 getResponseHeader()
方法只能拿到一些最基本的响应头,Cache-Control
、Content-Language
、Content-Type
、Expires
、Last-Modified
、Pragma
,如果要访问其他头,则需要服务器设置本响应头。
Access-Control-Expose-Headers: <header-name>[, <header-name>]*
5.4 Access-Control-Max-Age
Access-Control-Max-Age
头指定了 preflight
请求的结果能够被缓存多久
5.5 Access-Control-Allow-Credentials
Access-Control-Allow-Credentials
头指定了当浏览器的 credentials
设置为 true
时是否允许浏览器读取 response
的内容。当用在对 preflight
预检测请求的响应中时,它指定了实际的请求是否可以使用 credentials
。
Access-Control-Allow-Credentials: true
5.6 Access-Control-Allow-Methods
Access-Control-Allow-Methods
标头字段指定了访问资源时允许使用的请求方法,用于预检请求的响应。其指明了实际请求所允许使用的 HTTP
方法。
5.7 Access-Control-Allow-Headers
Access-Control-Allow-Headers
标头字段用于预检请求的响应。其指明了实际请求中允许携带的标头字段。这个标头是服务器端对浏览器端 Access-Control-Request-Headers
标头的响应。
五、问题
5.1 Request header field xfilesize is not allowed by Access-Control-Allow-Headers
问题
原因: 包含自定义Header
字段的跨域请求,浏览器会先向服务器发送OPTIONS
请求,探测该服务器是否允许自定义的跨域字段。如果允许,则继续实际的POST/GET正常请求,否则,返回标题所示错误。
解决:
-
Options 请求为:
Access-Control-Request-Headers: content-type
-
那么,服务端必须作出应答:
Access-Control-Allow-Headers: Content-Type