【本期嘉宾介绍】睿得,具有多年研发、运维、安全等IT相关从业经历。目前从事CDN、存储、视频直播点播的技术支持。喜爱钻研,喜爱编码,喜爱分享。
CORS(Cross-Origin Resource Sharing 跨源资源共享),当一个请求url的协议、域名、端口三者之间任意一与当前页面地址不同即为跨域。
例如最常见的,在一个域名下的网页中,调用另一个域名中的资源。
相对于上面这种静态的调用方式,还可以通过Ajax技术来动态发起跨域请求。例如如下的方式,利用XMLHttpRequest对象发送一个GET请求,获取另一个域名下的图片内容。
<html>
<head>CORS Testhead>
<body>
<div id="img_Div">div>
<script type="text/javascript">
//XmlHttpRequest对象
function createXmlHttpRequest(){
if(window.ActiveXObject){ //如果是IE浏览器
return new ActiveXObject("Microsoft.XMLHTTP");
}else if(window.XMLHttpRequest){ //非IE浏览器
return new XMLHttpRequest();
}
}
function getFile() {
var img_Container = document.getElementById("img_Div");
var xhr = createXmlHttpRequest();
xhr.open('GET', 'http://oss.youkouyang.com/1.jpg', true);
xhr.setRequestHeader('Content-Type', 'image/jpeg');
xhr.responseType = "blob";
xhr.onload = function() {
if (this.status == 200) {
var blob = this.response;
var img = document.createElement("img");
img.onload = function(e) {
window.URL.revokeObjectURL(img.src);
};
img.src = window.URL.createObjectURL(blob);
img_Container.appendChild(img);
}
}
xhr.send(null);
}
script>
<div class="row">
<input type="button" onclick="getFile()" value="Get" />
div>
body>
html>
为了改善网络应用程序,开发人员要求浏览器供应商允许跨域请求。跨域请求主要用于:
跨域请求和Ajax技术都会极大地提高页面的体验,但同时也会带来安全的隐患,其中最主要的隐患来自于CSRF(Cross-site request forgery)跨站请求伪造。
CSRF攻击的大致原理是:
出于安全原因,浏览器限制从脚本中发起的跨域HTTP请求。默认的安全限制为同源策略, 即JavaScript或Cookie只能访问同域下的内容。
W3C推荐了一种跨域的访问验证的机制,即CORS(Cross-Origin Resource Sharing 跨源资源共享)。
这种机制让Web应用服务器能支持跨站访问控制,使跨站数据传输更加安全,减轻跨域HTTP请求的风险。
CORS验证机制需要客户端和服务端协同处理。
基于上述的CSRF的风险,各主流的浏览器都会对动态的跨域请求进行特殊的验证处理。验证处理分为简单请求验证处理和预先请求验证处理。
当请求同时满足下面两个条件时,浏览器会直接发送GET请求,在同一个请求中做跨域权限的验证。
请求方法是下列之一:
请求头中的Content-Type请求头的值是下列之一:
简单请求时,浏览器会直接发送跨域请求,并在请求头中携带Origin 的header,表明这是一个跨域的请求。
服务器端接到请求后,会根据自己的跨域规则,通过Access-Control-Allow-Origin和Access-Control-Allow-Methods响应头,来返回验证结果。
如果验证成功,则会直接返回访问的资源内容。
如果验证失败,则返回403的状态码,不会返回跨域请求的资源内容。
可以通过浏览器的Console查看具体的验证失败原因
当请求满足下面任意一个条件时,浏览器会先发送一个OPTION请求,用来与目标域名服务器协商决定是否可以发送实际的跨域请求。
请求方法不是下列之一:
请求头中的Content-Type请求头的值不是下列之一:
浏览器在发现页面中有上述条件的动态跨域请求的时候,并不会立即执行对应的请求代码,而是会先发送Preflighted requests(预先验证请求),Preflighted requests是一个OPTION请求,用于询问要被跨域访问的服务器,是否允许当前域名下的页面发送跨域的请求。
OPTIONS请求头部中会包含以下头部:Origin、Access-Control-Request-Method、Access-Control-Request-Headers。
服务器收到OPTIONS请求后,设置Access-Control-Allow-Origin、Access-Control-Allow-Method、Access-Control-Allow-Headers头部与浏览器沟通来判断是否允许这个请求。
如果Preflighted requests验证通过,浏览器才会发送真正的跨域请求。
如果Preflighted requests验证失败,则会返回403状态,浏览器不会发送真正的跨域请求。
可以通过浏览器的Console查看具体的验证失败原因
默认情况下,跨源请求不提供凭据(cookie、HTTP认证及客户端SSL证明等)。通过将withCredentials属性设置为true,可以指定某个请求应该发送凭据。xhr.withCredentials = true;
如果服务器接收带凭据的请求,会用下面的HTTP头部来响应。
Access-Control-Allow-Credentials: true
服务器还可以在Preflight响应中发送这个HTTP头部,表示允许源发送带凭据的请求。
如果发送的是带凭据的请求,但服务器的响应中没有包含这个头,那么浏览器就不会把响应交给JavaScript(responseText中将是空字符串,size为0)。
注意,当withCredentials属性设置为true,需要response header中的'Access-Control-Allow-Origin'为一个确定的域名,而不能使用'*'这样的通配符。
服务器端对于跨域请求的处理流程如下:
注意:由于CDN的缓存特性,CDN配合OSS时,需要在CDN中设置CORS配置。