CORS讲解
跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。
什么情况下需要 CORS ?
XMLHttpRequest
或 Fetch 发起的跨域 HTTP 请求。- Web 字体 (CSS 中通过
@font-face
使用跨域字体资源), 因此,网站就可以发布 TrueType 字体资源,并只允许已授权网站进行跨站调用。 - WebGL 贴图
- 使用
drawImage
将 Images/video 画面绘制到 canvas - 样式表(使用 CSSOM)
功能概述
跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。
另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET
以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST
请求),
浏览器必须首先使用 OPTIONS
方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。
服务器确认允许之后,才发起实际的 HTTP 请求。
在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies和 HTTP 认证相关数据)。
若干访问控制场景
简单请求
某些请求不会触发 CORS 预检请求。本文称这样的请求为“简单请求”。
若请求满足所有下述条件,则该请求可视为“简单请求”:
- 使用下列方法之一:
- Fetch 规范定义了对 CORS 安全的首部字段集合,不得人为设置该集合之外的其他首部字段。该集合为:
Content-Type
的值仅限于下列三者之一:text/plain
multipart/form-data
application/x-www-form-urlencoded
- 请求中的任意
XMLHttpRequestUpload
对象均没有注册任何事件监听器;XMLHttpRequestUpload
对象可以使用XMLHttpRequest.upload
属性访问。 - 请求中没有使用
ReadableStream
对象。
注意: 这些跨域请求与浏览器发出的其他跨域请求并无二致。如果服务器未返回正确的响应首部,则请求方不会收到任何数据。因此,那些不允许跨域请求的网站无需为这一新的 HTTP 访问控制特性担心。
比如说,假如站点 http://foo.example 的网页应用想要访问 http://bar.other 的资源。http://foo.example 的网页中可能包含类似于下面的 JavaScript 代码:
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/public-data/'; function callOtherDomain() {
if(invocation) {
invocation.open('GET', url, true);
invocation.onreadystatechange = handler;
invocation.send();
}
}
客户端和服务器之间使用 CORS 首部字段来处理跨域权限:
分别检视请求报文和响应报文:
GET /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/ Minefield/.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]
第 1~10 行是请求首部。第10行 的请求首部字段 Origin
表明该请求来源于 http://foo.exmaple
。
第 13~22 行是来自于 http://bar.other 的服务端响应。响应中携带了响应首部字段 Access-Control-Allow-Origin
(第 16 行)。
使用 Origin
和 Access-Control-Allow-Origin
就能完成最简单的访问控制。
本例中,服务端返回的 Access-Control-Allow-Origin: *
表明,该资源可以被任意外域访问。
如果服务端仅允许来自 http://foo.example 的访问,该首部字段的内容如下:
Access-Control-Allow-Origin: http://foo.example
现在,除了 http://foo.example,其它外域均不能访问该资源(该策略由请求首部中的 ORIGIN 字段定义,见第10行)。
Access-Control-Allow-Origin
应当为 * 或者包含由 Origin 首部字段所指明的域名。
预检请求
与前述简单请求不同,“需预检的请求”要求必须首先使用 OPTIONS
方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。
"预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响
请求满足下述任一条件时,即应首先发送预检请求:
- 使用了下面任一 HTTP 方法:
- 人为设置了对 CORS 安全的首部字段集合之外的其他首部字段。该集合为:
Content-Type
的值不属于下列之一:application/x-www-form-urlencoded
multipart/form-data
text/plain
- 请求中的
XMLHttpRequestUpload
对象注册了任意多个事件监听器。 - 请求中使用了
ReadableStream
对象。
如下是一个需要执行预检请求的 HTTP 请求:
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/post-here/';
var body = '<?xml version="1.0"?><person><name>Arun</name></person>'; function callOtherDomain(){
if(invocation)
{
invocation.open('POST', url, true);
invocation.setRequestHeader('X-PINGOTHER', 'pingpong');
invocation.setRequestHeader('Content-Type', 'application/xml');
invocation.onreadystatechange = handler;
invocation.send(body);
}
} ......
上面的代码使用 POST 请求发送一个 XML 文档,该请求包含了一个自定义的请求首部字段(X-PINGOTHER: pingpong)。
另外,该请求的 Content-Type 为 application/xml。因此,该请求需要首先发起“预检请求”。
.OPTIONS /resources/post-here/ 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/ Minefield/.1b3pre
.Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
5.Accept-Language: en-us,en;q=0.5
6.Accept-Encoding: gzip,deflate
7.Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
8.Connection: keep-alive
9.Origin: http://foo.example
10.Access-Control-Request-Method: POST
11.Access-Control-Request-Headers: X-PINGOTHER, Content-Type
12.
13.
14.HTTP/1.1 200 OK
15.Date: Mon, 01 Dec 2008 01:15:39 GMT
16.Server: Apache/2.0.61 (Unix)
17.Access-Control-Allow-Origin: http://foo.example
18.Access-Control-Allow-Methods: POST, GET, OPTIONS
19.Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
20.Access-Control-Max-Age: 86400
21.Vary: Accept-Encoding, Origin
22.Content-Encoding: gzip
23.Content-Length: 0
24.Keep-Alive: timeout=2, max=100
25.Connection: Keep-Alive
26.Content-Type: text/plain
预检请求完成之后,发送实际请求:
POST /resources/post-here/ 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/ Minefield/.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
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Referer: http://foo.example/examples/preflightInvocation.html
Content-Length: 55
Origin: http://foo.example
Pragma: no-cache
Cache-Control: no-cache <?xml version="1.0"?><person><name>Arun</name></person> HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://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 GZIP'd payload]
浏览器检测到,从 JavaScript 中发起的请求需要被预检。从上面的报文中,我们看到,第 1~12 行发送了一个使用 OPTIONS 方法的“
预检请求”。
OPTIONS 是 HTTP/1.1 协议中定义的方法,用以从服务器获取更多信息。
该方法不会对服务器资源产生影响。 预检请求中同时携带了下面两个首部字段:
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
首部字段 Access-Control-Request-Method 告知服务器,实际请求将使用 POST 方法。
首部字段
Access-Control-Request-Headers 告知服务器,实际请求将携带两个自定义请求首部字段:
X-PINGOTHER 与 Content-Type。
服务器据此决定,该实际请求是否被允许。
第14~26 行为预检请求的响应,表明服务器将接受后续的实际请求。重点看第 17~20 行:
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age:
首部字段 Access-Control-Allow-Methods
表明服务器允许客户端使用
POST,
GET
和 OPTIONS
方法发起请求。
首部字段 Access-Control-Allow-Headers
表明服务器允许请求中携带字段
与X-PINGOTHER
。 Content-Type
最后,首部字段 Access-Control-Max-Age
表明该响应的有效时间为 86400 秒,也就是 24 小时。在有效时间内,浏览器无须为同一请求再次发起预检请求。
预检请求与重定向
大多数浏览器不支持针对于预检请求的重定向。如果一个预检请求发生了重定向,浏览器将报告错误:
The request was redirected to 'https://example.com/foo', which is disallowed for cross-origin requests that require preflight
Request requires preflight, which is disallowed to follow cross-origin redirect
在浏览器的实现跟上规范之前,有两种方式规避上述报错行为:
- 在服务端去掉对预检请求的重定向;
- 将实际请求变成一个简单请求。
如果上面两种方式难以做到,我们仍有其他办法:
- 发出一个简单请求(使用 Response.url 或 XHR.responseURL)以判断真正的预检请求会返回什么地址。
- 发出另一个请求(真正的请求),使用在上一步通过Response.url 或 XMLHttpRequest.responseURL获得的URL。
不过,如果请求是由于存在 Authorization 字段而引发了预检请求,则这一方法将无法使用。这种情况只能由服务端进行更改
附带身份凭证的请求
Fetch 与 CORS 的一个有趣的特性是,可以基于 HTTP cookies 和 HTTP 认证信息发送身份凭证。
一般而言,对于跨域 XMLHttpRequest
或 Fetch 请求,浏览器不会发送身份凭证信息。
如果要发送凭证信息,需要设置 XMLHttpRequest
的某个特殊标志位。
本例中,http://foo.example 的某脚本向 http://bar.other 发起一个GET 请求,并设置 Cookies:
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/credentialed-content/'; function callOtherDomain(){
if(invocation) {
invocation.open('GET', url, true);
invocation.withCredentials = true;
invocation.onreadystatechange = handler;
invocation.send();
}
}
第 7 行将 XMLHttpRequest
的 withCredentials 标志设置为 true,
从而向服务器发送 Cookies。
因为这是一个简单 GET 请求,所以浏览器不会对其发起“预检请求”。
但是,如果服务器端的响应中未携带 Access-Control-Allow-Credentials: true ,浏览器将不会把响应内容返回给请求的发送者。
客户端与服务器端交互示例如下:
GET /resources/access-control-with-credentials/ 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/ Minefield/.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/credential.html
Origin: http://foo.example
Cookie: pageAccess=2 HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:34:52 GMT
Server: Apache/2.0.61 (Unix) PHP/4.4.7 mod_ssl/2.0.61 OpenSSL/0.9.7e mod_fastcgi/2.4.2 DAV/2 SVN/1.4.2
X-Powered-By: PHP/5.2.6
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Credentials: true
Cache-Control: no-cache
Pragma: no-cache
Set-Cookie: pageAccess=3; expires=Wed, 31-Dec-2008 01:34:53 GMT
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 106
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain [text/plain payload]
即使第 11 行指定了 Cookie 的相关信息,但是,如果 bar.other 的响应中缺失 Access-Control-Allow-Credentials
: true(
第 19 行),则响应内容不会返回给请求的发起者。
附带身份凭证的请求与通配符
对于附带身份凭证的请求,服务器不得设置 Access-Control-Allow-Origin 的值为“*”。
这是因为请求的首部中携带了 Cookie 信息,如果 Access-Control-Allow-Origin 的值为“*”,请求将会失败。
而将 Access-Control-Allow-Origin 的值设置为
http://foo.example,则请求将成功执行。
另外,响应首部中也携带了 Set-Cookie 字段,尝试对 Cookie 进行修改。如果操作失败,将会抛出异常。
HTTP 响应首部字段
Access-Control-Allow-Origin
Access-Control-Allow-Origin: <origin> | *
其中,origin 参数的值指定了允许访问该资源的外域 URI。对于不需要携带身份凭证的请求,服务器可以指定该字段的值为通配符,表示允许来自所有域的请求。
Access-Control-Expose-Headers
在跨域访问时,XMLHttpRequest对象的getResponseHeader()方法只能拿到一些最基本的响应头,Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,如果要访问其他头,则需要服务器设置本响应头。
Access-Control-Expose-Headers
头让服务器把允许浏览器访问的头放入白名单,例如:
Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header
这样浏览器就能够通过getResponseHeader访问X-My-Custom-Header
和 X-Another-Custom-Header
响应头了。
Access-Control-Max-Age
Access-Control-Max-Age
头指定了preflight请求的结果能够被缓存多久
Access-Control-Max-Age: <delta-seconds>
delta-seconds
参数表示preflight请求的结果在多少秒内有效。
Access-Control-Allow-Credentials
Access-Control-Allow-Credentials
头指定了当浏览器的credentials
设置为true时是否允许浏览器读取response的内容。
当用在对preflight预检测请求的响应中时,它指定了实际的请求是否可以使用credentials
。
请注意:简单 GET 请求不会被预检;如果对此类请求的响应中不包含该字段,这个响应将被忽略掉,并且浏览器也不会将相应内容返回给网页。
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods
Access-Control-Allow-Methods
首部字段用于预检请求的响应。其指明了实际请求所允许使用的 HTTP 方法。
Access-Control-Allow-Methods: <method>[, <method>]*
Access-Control-Allow-Headers
Access-Control-Allow-Headers
首部字段用于预检请求的响应。其指明了实际请求中允许携带的首部字段。
Access-Control-Allow-Headers: <field-name>[, <field-name>]*
HTTP 请求首部字段
略
做记录
参考网址
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS
CORS讲解的更多相关文章
- cowboy跨域请求处理
这几日在使用cowboy开发https服务器的过程中碰到几个问题,这里随手记录一下. 1)如果返回错误ERR_EMPTY_RESPONSE,那么可能是web服务器被关闭了. 2)如果返回错误ERR_C ...
- 15 张精美动图全面讲解 CORS
前言: 本文翻译自 Lydia Hallie 小姐姐写的
- CORS详解[译]
介绍 由于同源策略的缘故,以往我们跨域请求,会使用诸如JSON-P(不安全)或者代理(设置代理和维护繁琐)的方式.而跨源资源共享(Cross-Origin Resource Sharing)是一个W3 ...
- CORS简介
现在请跟我做:在您的浏览器的地址栏中输入www.yhd.com并敲击回车.在网站内容全部加载完毕后,按F12打开浏览器的调试窗口.当切换到Sources页时,您会发现您当前所看到的一号店的页面是从多个 ...
- CORS详解
介绍 由于同源策略的缘故,以往我们跨域请求,会使用诸如JSON-P(不安全)或者代理(设置代理和维护繁琐)的方式.而跨源资源共享(Cross-Origin Resource Sharing)是一个W3 ...
- (转载)构建public APIs与CORS
from: https://segmentfault.com/a/1190000000709909 理由:在操作层面详细的讲解了跨域的操作.尤其是对于option请求的详解.收藏. 在构建Public ...
- WeX5 - AJAX跨域调用相关知识-CORS和JSONP
http://docs.wex5.com/ajax-cross-domain/ 1.什么是跨域 跨域问题产生的原因,是由于浏览器的安全机制,JS只能访问与所在页面同一个域(相同协议.域名.端口)的内容 ...
- AJAX跨域调用相关知识-CORS和JSONP(引)
AJAX跨域调用相关知识-CORS和JSONP 1.什么是跨域 跨域问题产生的原因,是由于浏览器的安全机制,JS只能访问与所在页面同一个域(相同协议.域名.端口)的内容. 但是我们项目开发过程中,经常 ...
- AJAX跨域调用相关知识-CORS和JSONP
1.什么是跨域 跨域问题产生的原因,是由于浏览器的安全机制,JS只能访问与所在页面同一个域(相同协议.域名.端口)的内容. 但是我们项目开发过程中,经常会遇到在一个页面的JS代码中,需要通过AJAX去 ...
随机推荐
- 证书转化 .cer .crt .jks
cer格式——>JKS (keytool 为java JDK自带的,可以在bin目录下找到) keytool -import -alias mycert -file d:\def.cer -ke ...
- Hadoop(五)—— HDFS NameNode、DataNode工作机制
一.NN与2NN工作机制 NameNode(NN) 1.当HDFS启动时,会加载日志(edits)和镜像文件(fsImage)到内存中. 2-4.当元数据的增删改查请求进来时,NameNode会先将操 ...
- 大数据应用期末总评Hadoop综合大作业
作业要求来源于:https://edu.cnblogs.com/campus/gzcc/GZCC-16SE2/homework/3339 1.将爬虫大作业产生的csv文件上传到HDFS 此次作业选取的 ...
- PokemonGo-LBS AR项目实战
<Pokemon GO>在2016年发布,立即成为一款现象级手游,时至今日也是最成功的AR游戏!<Pokemon GO>目前仍旧吸引全球范围内大批玩家入驻,而这也使得该款游戏创 ...
- pycharm项目添加.gitignore忽略.idea文件夹
本地项目结构: .gitignore文件中添加: at_alsv_pro/.idea/SearchImage.iml at_alsv_pro/.idea/misc.xml at_alsv_pro/.i ...
- 几种常见的java网页静态化技术对比
名称 优点 缺点 使用场景 jsp 1.功能强大,可以写java代码 2.支持jsp标签(jsp tag) 3.支持表达式语言(el) 4.官方标准,用户群广,丰富的第三方jsp标签库 5.性能良好. ...
- 【计算机视觉】图像着色(Image Colorization)
(Sometimes technology enhances art. Sometimes it vandalizes art. --- 有时技术会增强艺术,有时它破坏了艺术.) 着色黑白电影是一个可 ...
- PS弧形边缘的去黑色背景色
按照理论来说,纯色的字体加上纯色的背景,然后保存成png文件,然后用色彩范围选择纯色的背景,去掉背景,这样应该能得到原来设置的纯色的字体,但实际测试后不是这样的.如果是矩形等,是纯色,但是Photos ...
- [LeetCode] 298. Binary Tree Longest Consecutive Sequence 二叉树最长连续序列
Given a binary tree, find the length of the longest consecutive sequence path. The path refers to an ...
- LeetCode:缺失的第一个正数【41】
LeetCode:缺失的第一个正数[41] 题目描述 给定一个未排序的整数数组,找出其中没有出现的最小的正整数. 示例 1: 输入: [1,2,0] 输出: 3示例 2: 输入: [3,4,-1,1] ...