深入了解CORS
这篇文章是由同行评审Panayiotis«pvgr»Velisarakos 。 感谢所有SitePoint的审稿作出SitePoint内容也可以是最好的!
CORS是用HTML5来了一个相对较新的API,让我们的网站,要求外部和以前受限资源。 它放宽了传统的同源策略使我们可以通过要求上比我们的父页面不同的域资源。
例如,前CORS跨域,Ajax请求是不可能的(使从页面Ajax调用example.com/index.html
到anotherExample.com/index.html
)。
在这篇文章中,我们将看到如何使用CORS进一步与其他系统和网站互动的方式,以创造更好的Web体验。 探索CORS多之前,让我们先来看看它的浏览器支持它。
CORS和浏览器支持
Internet Explorer 8和只有通过9个支持CORS XDomainRequest类。 主要的区别是,而不是做一个正常的实例化的东西,如var xhr = new XMLHttpRequest()
你将不得不使用var xdr = new XDomainRequest();
。
IE 11,Edge和最近的和不确实,最近所有的Firefox,Safari浏览器,Chrome浏览器版本,歌剧完全支持CORS。 IE10和Android的默认浏览器最多只4.3在用于图像时缺乏CORS支持<canvas>
元素。
据CanIuse, 人的92.61%,在全球有支持的浏览器这表明我们很可能不会去犯了一个错误,如果我们使用它。
制作简单的跨来源Ajax请求
现在我们知道,同样的原产地政策,禁止在不同领域的网站作出Ajax请求到其他领域,让我们看看我们如何能够绕过此,为了使一个跨域Ajax请求到另一个网站。
如果你只是尝试拍摄一个Ajax请求到一个随机的网站,这很可能会无法读取响应,除非另一个网站允许它。
<script>
var xhr = new XMLHttpRequest();
var url = "//example.com";
xhr.open("GET", url);
xhr.onreadystatechange = function() {
if (xhr.status === 200 && xhr.readyState === 4) {
document.querySelector("body").innerHTML = xhr.responseText
}
}
xhr.send();
</script>
如果你打开浏览器的控制台,你会得到类似的消息:
XMLHttpRequest的无法加载http://example.com 。 没有“访问控制允许来源”标头出现在所请求的资源。 原产地“ http://otherExampleSite.com因此”是不允许访问。
要成功地读取响应,你就必须设置一个名为访问控制允许来源头。 此报头必须被在应用程序的后端逻辑任一组(设定之前手动响应被传递到客户端的报头)或服务器的配置(例如编辑apache.conf
并添加Header set Access-Control-Allow-Origin "*"
给它,如果你使用Apache)。
添加与meta标签的头在你的文档<head>
标签这样是行不通的: <meta http-equiv="Access-Control-Allow-Origin" content="*">
这里是你如何启用所有起源于PHP(即请求资源网站)跨域请求:
当进行跨域请求,目标网站必须是谁启用您的起源,并允许您读取请求的响应的一个。
如果你想允许特定的原产地,你可以做这样的事情在PHP中:
header("Access-Control-Allow-Origin: http://example.com");
然而, Access-Control-Allow-Origin
包头本身不允许插在头部,无论分隔符多个主机。 这意味着,如果你想允许各种不同域的跨域请求,你必须动态地生成你的头。
例如,在PHP中你可以登录网站请求你的资源的来源以及它特定的白名单匹配,添加页眉,允许对特定的原产地进行跨域请求。 这里是一个硬编码的白名单中一个微小的例子:
一些安全保持在跨域请求和凭证(如饼干)的请求 - 响应交换期间不被泄漏。 此外,如果远程服务器不明确允许包括从其他网站上的跨域请求其网站的用户凭证和该网站没有明确声明它想要的用户凭据传递到远程服务器,那么网站发出请求将最有可能得到的是不是个性化的响应。 这是因为用户的会话cookie不会去请求和响应将不包含登录的用户从而降低CSRF及其他攻击相关的特定数据。
为了让事情变得更简单,让我们说,我们有两个网站。 第一个设置一个cookie,每当用户进入,就说明这应该是他的名字的cookie的值。 其他网站做一个跨域Ajax请求,并增加了其DOM的响应。
获取页面的用户看到它与CORS
如果我们想包括与远程请求的用户凭据,我们要做的两个变化,首先在网站上发出请求和接收请求第二次在该网站的代码。 在网站上提出请求,我们必须设置withCredentials
Ajax请求的财产true
:
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
远程服务器本身,除了让我们的起源,必须设置Access-Control-Allow-Credentials
头,并将其值设置为true
。 使用数字1或0是行不通的。
如果我们简单地设置withCredentials
至true
,但服务器没有设置上述头,我们将无法获得响应,即使我们的起源是允许的。 我们会得到一个类似的消息:
XMLHttpRequest的无法加载http://example.com/index.php 。 证书标志为“真”,但“访问控制允许的凭据”标头”。 它必须是“真”,让凭据。 原产地“ 的http://本地主机:63342 ”,因此没有允许访问。
如果两个更改,我们将得到一个个性化的响应。 在我们的例子中,我们存储在cookie中的用户的名称将是远程服务器返回到我们的网站的响应。
然而,允许凭据传递到跨域请求是相当危险的,因为它开辟了各种攻击,如CSRF(跨站请求伪造),XSS(跨站脚本)的可能性,攻击者便可利用用户的登录的用户身份采取无需用户知道在远程服务器操作(如取钱如果远程服务器是银行网站)。
Preflights
当要求开始越来越复杂,我们可能想知道,如果一个特定的请求方法(如get
, put
, post
, patch
或delete
)或特定的自定义标题是允许的,被服务器接受。 在这种情况下,您可能需要使用preflights您第一次发送带有请求options
方法,并声明有什么方法和标题您的要求都会有。 然后,如果服务器返回CORS标头,我们看到我们的起源,页眉和请求方法是允许的,我们可以使实际的请求(原产地是通过我们的浏览器与每一个跨域请求,我们做出过一个头,而且没有,使得在一个典型的浏览器的请求时,我们不能改变原产地的价值)。
正如我们可以在上面的图片中看到,服务器返回几个头,我们可以在确定是否进行实际的请求使用。 所有的起源是允许的(它返回到我们Access-Control-Allow-Origin: *
,虽然传递用户凭据我们不能请求( Access-Control-Allow-Credentials
),我们只能让get
请求( Access-Control-Allow-Methods
),那就可以使用X-ApiKey自定义页眉( Access-Control-Allow-Headers
)。最后, Access-Control-Max-Age
头节目以秒为单位的值,精确定位的时间(从请求的时间),我们可以提出请求,而不依赖于另一个预检。
在另一方面,在我们的前端逻辑,我们通过Access-Control-Request-Method
和我们通过Access-Control-Request-Headers
,以指示什么样的请求方法和什么样的头,我们打算加入到我们的真正的请求。 在香草的JavaScript,你可以将制作使用Ajax调用时头xhr.setRequestHeader(“headerString”,“headerValueString”); 。
CORS的帆布图片
如果我们想加载外部图像和编辑它们在画布或只是保存作为缓存机制的localStorage的base64编码值,远程服务器必须启用CORS。 有此可以做不同的方式。 一种方法是编辑您的Web服务器的配置以添加Access-Control-Allow-Origin
为特定图像类型的每一个请求头,这样的例子在显示Mozilla的文档 。 如果我们具有通过改变动态地生成图像的脚本Content-Type
并输出图像如http://example.com/image/image.php?image=1我们可以简单地设置,与输出所述图像一起报头。
如果没有CORS,如果我们试图访问远程图像,用它加载到画布上,编辑并保存toDataURL
或只是尝试修改后的图像添加到使用DOM toDataURL
,我们将得到下面的安全异常(我们将不会能够保存或显示它): 从原点“图像http://example.com ”已加载的跨来源资源共享政策阻止:无“访问控制允许来源”标头出现在请求资源。 原产地“ 的http://本地主机:63342 ”,因此没有允许访问 。
如果该图像是返回一个沿图像服务器Access-Control-Allow-Origin: *
标题,然后我们可以做到以下几点:
var img = new Image,
canvas = document.createElement("canvas"),
ctx = canvas.getContext("2d"),
src = "http://example.com/test/image/image.php?image=1";
img.setAttribute('crossOrigin', 'anonymous');
img.onload = function() {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage( img, 0, 0 );
ctx.font = "30px Arial";
ctx.fillStyle = "#000";
ctx.fillText("#SitePoint",canvas.width / 3,canvas.height / 3);
img.src = canvas.toDataURL();
document.querySelector("body").appendChild(img);
localStorage.setItem( "savedImageData", canvas.toDataURL("image/png") );
}
img.src = src;
这将加载的外部图像,添加在其中的文本#SitePoint和两个显示它给用户,并将其保存在localStorage的。 请注意,我们将外部图像的crossOrigin属性- img.setAttribute('crossOrigin', 'anonymous');
。 此属性是强制性的,如果我们不把它添加到外在形象,我们仍然会得到另一个安全异常。
该Crossorigin属性
当我们为外部图像,音频,视频,使用适当的HTML样式表和脚本的请求(5)标签,我们都赚不到一CORS请求。 这意味着没有Origin
标头发送到服务于外部资源的页面。 如果没有CORS,我们就无法编辑在画布上,查看异常和错误日志外部图像从外部脚本,我们的网站负载,或使用CSS对象模型与外部样式表等工作时。 还有当我们要使用这些功能,某些情况下,这是在crossorigin
我们上面提到的属性就派上用场了。
所述crossorigin
属性可以被设置为元素如<link>
, <img>
和<script>
当我们的属性添加到这样的元素,我们确保一个CORS请求将与制造Origin
头设置不当。 如果外部资源可以通过您的起源Access-Control-Allow-Origin
头部非CORS请求的限制将不适用。
该crossorigin
属性有两个可能的值:
-
anonymous
-设定crossorigin
属性为这个值将使CORS,而没有经过用户的凭据对外部资源(类似于做一个Ajax请求,CORS不添加请求withCredentials
属性)。 -
use-credentials
-设定crossorigin
属性为这个值将使CORS请求对外部资源有可能存在该资源的任何用户凭据一起。 对于这项工作,该服务器不仅要设置Access-Control-Allow-Origin
头,让您的Origin
,但它也必须设置Access-Control-Allow-Credentials
,以true
。
用户凭证包括饼干,HTTP基本认证证书,证书和当用户请求一个特定的网站,发送其它用户数据。
结论
CORS使开发人员能够进一步与其他系统和网站互动的方式,以创造更好的Web体验。 它可以通过常用的HTML标签,以及制成具有像Ajax的Web 2.0技术传统的请求中使用一起。
你一直在你的项目中使用CORS? 你有它的困难? 我们想知道你的这印象至今。
参考和进一步阅读:
上一篇: 关于List中的大大坑(二)