什么是同源策略
同源政策(same-origin policy)是指同域名(或ip),同端口,同协议视为同一个域,同域内的脚本只能读写本域内的资源,而无法访问其它域的资源,这种安全限制称为同源策略。当一个浏览器的两个tab页中分别打开来百度和谷歌的页面当浏览器的百度tab页执行一个脚本的时候会检查这个脚本是属于哪个页面的,即检查是否同源,只有和百度同源的脚本才会被执行。如果非同源,那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。
同域指的是?
同协议:如都是http或者https
同域名:如都是http://baidu.com/a 和http://baidu.com/b
同端口:如都是8080端口
如果不是同源的,会受到三种限制:
1.Cookie、LocalStorage 和 IndexDB 无法读取。
2. DOM 无法获得。
3.AJAX 请求不能发送。
什么是跨域?跨域有几种实现形式
跨域
跨域是指从一个域名的网页去请求另一个域名的资源。比如从百度页面去请求谷歌的资源。跨域的严格一点的定义是:只要 协议,域名,端口有任何一个的不同,就被当作是跨域。
跨域有几种实现形式
一.JSONP
JSONP是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,兼容性好。主要利用html中script标签可以引入其他域的js文件,利用这个特性可以实现跨域访问接口,需要后端的支持。实现步骤:
1.定义数据处理函数fun:
2.网页通过添加一个<script>元素,src的地址执行后端接口最后加个参数callback=fun,向服务器请求JSON数据,这种做法不受同源政策限制;
3.服务器收到请求后,将数据放在fun回调函数里传回来,输出fun(data):
4.fun(data)会放到script标签作为js执行,此时会调用fun(data),将data作为参数。
定义处理函数
function foo(data){
console.log(data.ip)
}
网页动态插入<script>元素,由它向跨源网址发出请求。
function addScriptTag(src) {
var script = document.createElement('script');
script.src = src;
document.head.appendChild(script);
document.head.removeChild(script);
}
window.onload = function () {
addScriptTag('http://example.com/ip?callback=foo');
}
服务器收到这个请求以后,会将数据放在回调函数的参数位置返回
foo({
"ip":"10.64.25.83"
})
由于<script>元素请求的脚本,直接作为代码运行。这时,只要浏览器定义了foo函数,该函数就会立即调用。作为参数的JSON数据被视为JavaScript对象,而不是字符串,因此避免了使用JSON.parse的步骤。
缺点:
1.只能通过GET方式请求,参数长度有限制,安全性比较差
2.需要后端的支持
二.CORS
CORS全称是"跨域资源共享"(Cross-origin resource sharing)。
它允许浏览器向跨源服务器,发出XMLHttpRequest请求。支持现代浏览器,IE10以上浏览器。CORS需要浏览器和服务器的支持,因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
当CORS请求满足下面的条件时
1.请求方法是以下三种方法之一:
HEAD
GET
POST
2.HTTP的头信息不超出以下几种字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
基本思想是
1.当使用XMLHttpRequest()发送请求的时候,浏览器发现该请求不符合同源策略,会给该请求的头部信息添加一个origin字段,Origin字段用来说明,本次请求来自哪个源,服务器根据这个值,决定是否同意这次请求。
如果Origin指定的源,不被服务器允许,服务器也会返回正常的HTTP响应,浏览器发现响应头没有包含origin字段,就抛出错误,被onerror回调函数捕获,这种错误状态码无法识别。
2.如果指定的源,被服务器允许,服务器返回响应信息的响应头会包含origin的信息,如下:
三.document.domain(也就是降域)
document.domain用于主域相同子域不同的场景
降域的设置也是有限制的,只能把document.domain,设置成自身或者更高一级的域,且主域必须相同,如:a.b.test.com中的某个文档的域可以设置成a.b.test.com、b.test.com、test.com。但是不可以设置成.com或者c.a.b.test.com或者baidu.com,因为baidu.com主域和当前域不同了。
使用方法
a页面中加入document.domain = ‘test.com’;
b页面中加入document.domain = ‘test.com’;
a.index.html
<div class="ct">
<h1>使用降域实现跨域</h1>
<div class="main">
<input type="text" placeholder="http://a.test.com:8080/a.html">
</div>
<iframe src="http://b.test.com:8080/b.html" frameborder="0" ></iframe>
</div>
<script>
document.querySelector('.main input').addEventListener('input', function(){
console.log(this.value);
window.frames[0].document.querySelector('input').value = this.value;
})
document.domain = "test.com"
b.index.html
<div>
<input id="input" type="text" placeholder="http://b.test.com:8080/b.html">
</div>
<script>
document.querySelector('#input').addEventListener('input', function(){
window.parent.document.querySelector('input').value = this.value;
})
document.domain = 'test.com';
</script>
</html>
四,postMessage
postMessage是html5新增的方法,可以实现跨文本档、多窗口、跨域消息传递。该方法可以通过绑定window的message事件来监听发送跨文档消息传输内容。postMessage(data,origin)方法接受两个参数:
1.data:要传递的数据。
2.origin:字符串参数,指明目标窗口的源,协议+主机+端口号[+URL],URL会被忽略,所以可以不写,这个参数是为了安全考虑postMessage()方法只会将message传递给指定窗口,如果设置为"*",这样可以传递给任意窗口。
http://a.test.com/a.html
<div class="main">
<input type="text" placeholder="http://a.test.com/a.html">
</div>
<iframe src="http://b.test.com/b.html" frameborder="0" ></iframe>
<script>
$('.main input').addEventListener('input', function(){
console.log(this.value);
window.frames[0].postMessage(this.value,'*');
})
window.addEventListener('message',function(e) {
$('.main input').value = e.data
console.log(e.data);
});
function $(id){
return document.querySelector(id);
}
</script>
在http://a.test.com/a.html通过postMessage()方法向跨域的iframe页面http://b.test.com/b.html传递消息,b.html监听window的message事件就可以
<input id="input" type="text" placeholder="http://b.test.com/b.html">
<script>
$('#input').addEventListener('input', function(){
window.parent.postMessage(this.value, '*');
})
window.addEventListener('message',function(e) {
$('#input').value = e.data
console.log(e.data);
});
function $(id){
return document.querySelector(id);
}
</script>
JSONP 的原理是什么
JSONP不是最新的数据格式,是json跨域获取的解决方案,通过jsonp获取的数据作为js的参数运行。浏览器有同源策略,会把跨域请求都禁止了,但是html的<script>标签,不受同源策略的影响,可以从其他源获取数据,所以我们可以通过script标签,引入其他源的数据,通过js代码进行解析。先定义处理函数functionname,然后通过添加一个<script>元素,src的地址是后端接口,在地址后加个请求参数callback=函数名,这个就是处理函数的函数名,向服务器请求JSON数据,服务器收到请求后,把数据放在回调函数中返回,返回的functionname(data)会放在script标签作为js执行,所以会调用functionname(data),将data作为函数的参数。
CORS是什么
CORS是跨源资源分享(Cross-Origin Resource Sharing)的缩写。它是W3C标准,是跨源AJAX请求的根本解决方法。相比JSONP只能发GET请求,CORS允许任何类型的请求。CORS需要浏览器和服务器同时支持,现代浏览器都支持此功能,IE浏览器要IE10以上。CORS的整个通信过程不需要用户参与,浏览器会自己完成,CORS与AJAX的同源通信一样,在发送AJAX请求的时候,浏览器会自动在头部添加一个origin字段,只要服务器允许cors的约定,就可以实现跨域通信。
CORS两种请求
cors有简单请求和非简单请求,只要满足
1)请求方法是HEAD/GET/POST三种方法之一;
2)HTTP的头信息不超出一下几种字段:Accept/Accept-Encoding/Accept-Language/Cache-Control/Connection/Cookie/Host/If-Modified-Since/Referer/User-Agent/Content-Type/Content-Language。其中Content-Type仅限于三个值:application/x-www-form-urlencoded、multipart/form-data、text/pain。就是简单请求,如果不满足就是非简单请求,
简单请求
对于简单请求,浏览器直接发cors请求,在头部添加origin字段,origin字段用来说明本次请求来自哪个源,服务器会根据这个origin,决定是否同意通信,如果服务器不同意,也会返回正常的http响应,浏览器没发现有origin这个字段,就是报错,表示不能跨域通信。
** 非简单请求**
非简单请求是对服务器有特殊要求的请求,比如请求方法是PUT/DELETE,或者Content-Type字段的类型是application/json。
非简单请求的CORS请求,会在正式itongxin之前,增加爱一次HTTP查询请求,叫预检请求(preflight)。
浏览器现询问服务器,当前网页所在的域名是否在服务器的许可名单中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。
除了Origin字段,预检请求的头信息还包括两个特殊字段:
(1). Access-Control-Request-Method
该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,例子中是POST。
(2). Access-Control-Request-Headers
该字段是都好分割的字符串,指定浏览器CORS请求会额外发送的头信息字段,上面的例子默认application/json对应的额外字段是”Content-Type”。
服务器收到预检请求后,检查Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,并做出回应。
如果服务器否定了预检请求,会返回一个正常的HTTP Response,但是没有任何CORS相关的头信息字段。这时候浏览器认为i额服务器不同意预检请求,因此出发一个错误,被XMLHttpRequest对象的onerror毁掉函数捕获。控制台会打印出如下报错信息。
一旦服务器通过了预检请求,以后每次浏览器正常的CORS请求就都与简单请求一样,包括Origin字段信息。服务器的回应也会有Access-Control-Allow-Origin字段
根据视频里的讲解演示三种以上跨域的解决方式 ,写成博客
最后一个cors的介绍参考了阮一峰老师的日志。