第一次写博客,好紧张,不知道能写成啥样,哈哈哈。
自己的一知片解,有错请多多指教,嘻嘻嘻。
一、何为跨域?
只要协议、域名、端口后任何一个不同,就是跨域。
举个例子:
http://www.example.com | 协议不同 |
https://www.example.com | |
http://www.example.com | 域名不同 |
http://www.test.com | |
http://www.example.com | 端口不同 |
http://www.example.com:81 |
注意:ip相同,域名不同,也是跨域。(在本地写demo时,配置host文件,127.0.0.1配置了两个域名,纠结是否算跨域,试了一下,是的)
二、跨域的类型
1.cookie
只有同源的网页才能共享cookie
2.iframe
网格网页不同源,无法拿到对方的DOM
3.ajax
只能请求同源的网址,否则报错
三、解决跨域
我在我电脑的host文件里配置了两个域名模拟跨域。
以下,test1代表www.test1.myhost.com; test2代表www.test2.myhost.com
1.cookie
存在这样一种情况能够实现共享cookie:两个网页一级域名形同,二级域名不同,设置cookie时,指定两个网页的domain相同,便可以共享cookie啦。
test1:
document.cookie = 'key1= value1; domain=myhost.com'; console.log(document.cookie);//"key1 = value1"
test2:
console.log(document.cookie);//"key1 = value1"
就酱紫,他俩共享cookie了,好可怕
打开application看看,发现这cookie原来在myhost.com域名下。
2.iframe
一个页面中嵌套一个iframe,这个iframe的src与主页面跨域。这样的话,主页面获取操作iframe的DOM,iframe也无法操作主页面的DOM
test1为主页面,test2为嵌入的iframe
解决iframe跨域的,宝宝只知道这三种:
(1)片段识别符
(2)window.name
(3)window.postMessage
2.1 片段识别符
指的是URL中#后面的部分,只改变片段识别符,页面不会重新刷新。
实现思路:
父窗口可以把要传递的数据写入iframe的src片段识别符中,iframe通过监听hashchange事件得到通知,获取数据
同样道理,子窗口也可以改变父窗口的片段识别符,达到同样的效果。示例来喽:
父→子:
主页面
var origin = $('iframe').attr('src');$('iframe').attr('src', origin + '#主窗口');
iframe
console.log(window.location.hash);//"#主窗口"
子→父:
iframe
parent.location.href ='http://www.test1.myhost.com:8080/tutor/cookie#iframe'
主页面
console.log(window.location.hash);//"#iframe"
2.2 window.name
浏览器窗口有window.name属性,它的特点是无论是否同源,只要在同一个窗口中,前一个网页设置了这个属性,后一个网页就可以读取它
实现思路:
将iframe需要与主页面传递的数据写到iframe的window.name中,完成后将iframe的src设置成与主页面同源,此时,主页面与iframe就同源了,就可以拿到对方的数据啦,而iframe的src更改并不影响window.name的值啊,主页面便可以轻松读取iframe中window.name的值啦,是不是很机智!再来个例子吧。
iframe
window.name = '哈哈哈,我是iframe';location.href = 'http://www.test1.myhost.com:8080/这里就是一个与主页面同源的页面';
主页面
console.log($('iframe')[0].contentWindow.name);//"哈哈哈,我是iframe"
2.3 window.postMessage
html5引入的新API,允许跨窗口通信,不论这两个窗口是否同源。
window.postMessage(data, url); //data为要传给目标的数据,url为目标的url。
注意,此window是目标窗口的window,不是本窗口的window
同样来个例子:
子→父:
iframe
top.postMessage('hello', 'http://www.test1.myhost.com:8080/tutor/cookie');
主页面
window.addEventListener('message', function(e) { console.log(e.data);//"hello"}, false);
父→子:
主页面
$('.post').on('click', function() { $('iframe')[0].contentWindow.postMessage('hello hello', 'http://www.test2.myhost.com:8080/这里就是一个目标url');});
iframe
window.addEventListener('message', function(e) { console.log(e.data);//"hello hello"}, false);
这里的e有几个重要属性:
a.data:传递来的message
b.source:发送消息的窗口对象
c.origin:发送消息窗口的源(协议+主机+端口号)
3.ajax
诶嘿,宝宝知道的也是三种,哈哈哈,好巧啊
(1)JSONP
(2)websocket
(3)CORS
分别说说吧
3.1 JSONP
原理就是利用script脚本拥有跨域能力。
基本思想是,网页添加一个script元素,src放需要请求的接口,这种做法不受同源策略的限制。服务器收到请求后,将数据放在一个指定的回调函数里传回来。
这种方法简单适用,老式的浏览器全部支持,服务器改造也非常小。
js实现:
function addScriptTag(src) { var script = document.createElement('script'); script.setAttribute("type","text/javascript"); script.src = src; document.body.appendChild(script);}$('.ajax').on('click', function() { addScriptTag('http://www.php.myhost.com/jsonp.php?callback=foo');});function foo(data) { console.log('response data is: ' + data);};
jQuery实现:
$('.ajax').on('click', function() { $.ajax({ url: 'http://www.php.myhost.com/jsonp.php', type: 'get', dataType: 'jsonp', jsonpCallback: 'foo', data: {} });});function foo(data) { console.log('Your response is: ' + data);};
PHP代码:
3.2 websocket
websocket是一种通信协议。该协议不实行同源策略,只要服务器支持,就可以通过它进行跨源通信。
例如,长连接呀
其实不太懂,就先不说它了哈
3.3 CORS
开始扯啦
CORS是跨源资源分享(Cross-Origin Resource Sharing)的缩写。它是W3C标准,是跨源AJAX请求的根本解决方法。
CORS请求分为两类:简单请求和非简单请求,太多了,这里就先不介绍了,一查就知道了。
简单请求就是浏览器发出CORS请求时,http头当中增加一个域(origin)的信息。该域包含协议名、地址及一个可选的端口,用来说明本次请求来自哪个源,服务器根据这个值决定是否同意这次请求。(这都是浏览器代为发送,开发者的代码无法触及到)
如果这个指定的源不在许可范围内,服务器就返回一个正常的http回应。但是回应中没有Access-Control-Allow-Origin字段,抛出异常,XMLHttpRequest的onerror捕获。
如果这个指定的源在许可范围内,会多出几个头信息字段(都以Access-Contrl-开头)
非简单请求(我平常请求的接口都是非简单请求,因为content-type为application/json)是在发起正式通信之前增加一次http查询,实现询问服务器当前网页所在域名是否在许可名单中,得到肯定答复后浏览器才会发出正式的XMLHttpRequest请求,否则报错。
预检请求的方式是options,头信息里面关键字段是origin,表示来自哪个源。服务器收到预检请求后检查origin,确认是否允许跨源请求,就可以做出回应了。
允许请求:在http回应中,关键是Access-Control-Allow-Origin字段,如:Access-Control-Allow-Origin:http://api.com,表示http://api.com可以请求数据。设为*,表示同意任何跨域。
不允许请求:会返回一个正常的http回应,但没有任何CORS相关的头信息字段,浏览器会认为不同意预检请求,触发一个错误,被XMLHttpRequest的onerror捕获。
总而言之,普通跨域请求,只要服务端设置Access-Control-Allow-Origin即可,前端无须设置。带cookie请求,需要前后端都设置字段。注意,所带cookie为跨域请求接口所在域的cookie,而非当前页的。
查了一下前端带cookie请求咋实现:
js
var xhr = new XMLHttpRequest(); // 前端设置是否带cookie xhr.withCredentials = true;
jquery
$.ajax({ xhrFields: { withCredentials: true // 前端设置是否带cookie }, });
vue:
vue框架在vue-resource封装的ajax组件中加入以下代码:Vue.http.options.credentials = true
ok ,总算写完啦~~~
哦,对了,加一下ajax跨域的几种现象(这几种现象这个博客说的很好:http://www.cnblogs.com/dailc/p/5893341.html#crossDomain_crosPrinciple)