学习CORS之前,先看下如下问题,作为铺垫和准备。
问题1: 什么是跨域?
满足下面三个条件才会引发跨域问题:
问题2: 为什么要有跨域限制?
AJAX同源策略主要用来防止CSRF攻击。如果没有AJAX同源策略,相当危险,我们发起的每一次HTTP请求都会带上请求地址对应的cookie,那么可以做如下攻击:
问题3:为什么表单请求可以跨域
A页面(域名A)用 form 提交表单到B页面(域名B),A页面的脚本无法获取B页面中的内容,无法获得响应,浏览器认为是安全的。 其实,请求已经发送出去了,只是拿不到响应而已,AJAX 接收方可以读取响应内容的。所以,利用这个特性,依然有可能发起CSRF攻击。
问题4: 如何解决跨域问题?
<script src="b.com/b.js">, <img src="b.com/b.png"/>
引入b.com域名下的JavaScript文件或者图片。
jsonp就是动态创建script标签,然后利用src属性进行跨域的。// 定义一个fun函数
function foo (data) {
console.log(data);
};
// 创建一个脚本,并且告诉后端回调函数名叫foo
var body = document.getElementsByTagName('body')[0];
var script = document.gerElement('script');
script.type = 'text/javasctipt';
script.src = 'demo.js?callback=foo';
body.appendChild(script);
服务器接收到请求完成操作后,会调用callback函数(执行foo
)。
jsonp的缺点是:只能发起GET请求。因为,请求资源文件默认都是GET请求。
下面,详细讲解CORS跨域。
隶属于 W3C 的 Web 应用工作组( Web Applications Working Group )推荐了一种新的机制,即跨源资源共享(Cross-Origin Resource Sharing (CORS))。这种机制让Web应用服务器能支持跨站访问控制,从而使得安全地进行跨站数据传输成为可能。
CORS的请求有个明显标示,response header里面带有Access-Control-Allow-Origin
字段。
Access-Control-Allow-Origin: <origin> | *
origin参数指定一个允许向该服务器提交请求的URI。对于一个不带有credentials的请求,可以指定为'*',表示允许来自所有域的请求。 还可以指定具体的域,比如:
Access-Control-Allow-Origin: http://mozilla.com
如果服务器端指定了域名,而不是'*',那么请求头必须包含Origin。响应是根据请求头里的Origin的值来返回不同的内容的。
CORS的response里面还包含几个特殊的请求头:
access-control-allow-methods
:支持的HTTP请求方法access-control-allow-headers
:支持的request header类型,包括自定义header。access-control-allow-credentials
:是否支持携带cookies(后面会详细讲解)下面,我们具体分析下CORS支持的几种请求方式。
简单请求具备以下条件:
application/x-www-form-urlencoded
, multipart/form-data
或 text/plain
中的一种。如果服务器端仅允许来自 http://foo.example 的跨站请求,它可以返回:
Access-Control-Allow-Origin: http://foo.example
“预请求”要求必须先发送一个 OPTIONS 请求给目的站点,来查明这个跨站请求对于目的站点是不是安全可接受的。这样做,是因为跨站请求可能会对目的站点的数据造成破坏。 当请求具备以下条件,就会被当成预请求处理:
application/x-www-form-urlencoded
, multipart/form-data
或者 text/plain
以外的数据类型。比如说,用 POST 发送数据类型为application/xml
或者text/xml
的 XML 数据的请求。发起OPTIONS请求
预检成功后发起GET请求
如果request请求要支持HTTP Cookies和验证信息,那么,XMLHttpRequest
需要将withCredentials
属性设置为true
,而response需要返回Access-Control-Allow-Credentials: true
。
前端代码如下:
// JS
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();
}
// JQuery
$.ajax({
...
url: a_cross_domain_url,
xhrFields: {
withCredentials: true
}
});
目前最流行的跨域方式就是CORS了,需要服务端做相应配置,前端各类HTTP框架都支持了CORS机制。比如常用的axios
库,可以通过全局配置指定CORS相关属性。
axios.defaults.timeout = 10000;
axios.defaults.withCredentials = true;