JavaScript 跨域漫游
Posted 卷柏的花期
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript 跨域漫游相关的知识,希望对你有一定的参考价值。
前言:
最近在公司做了几个项目都涉及到了iframe,也就是在这些iframe多次嵌套的项目中,我发现之前对iframe的认识还是比较不足的,所以就静下心来,好好整理总结了iframe的相关知识:《Iframe 功能详解》。
在做公司项目的过程中,让我纠结之一的就是iframe的跨域问题,在网上查到的资料,基本都是给个思路,加个DEMO,也没有完整的解决方案。所以这里我结合公司的项目实际需求,从新整理了一下javascript跨域的相关方法。PS:请转载的童鞋,请注明出处 ...
目录:
一、 跨域简介
二、 iframe + domain
三、 iframe + hash
四、 iframe + name
五、 iframe + postMessage
六、 JSONP
七、 CROS
八、 Server 反向代理
九、 WebSocket
十、 flash
一. 跨域简介
“JS 跨域” 其实是一个“伪名词”。跨域实际上是浏览器基于安全考虑做出的“同源策略限制”。
这个策略早在1995年便由 Netscape 公司引入浏览器中,目前所有浏览器都实行这个机制。
“同源策略”的基本规则是:如果协议(protocol)、端口(port)、主机(host) 等,若有一个不同,浏览器便会限制不同域之间的通信与数据交换。
以 http://www.example.com 为例:
域名 | 是否跨域 | 原因 |
http://www.example/com/js/b.js | 否 | pathname |
https://www.example.com | 是 | protocol |
http://www.example:80.com | 是 | port |
http://a.example.com | 是 | host |
http://www.sample.com | 是 | host |
http://127.0.0.1 | 是 | host |
* 浏览器只会根据URL进行判断是否跨域,而不会去判断两个域名是否对应一个IP,更或者IP与域名之间的判断。
* 来自about:blank,javascript:和data:URLs中的内容,继承了将其载入的文档所指定的源,因为它们的URL本身未指定任何关于自身源的信息,
所以也并不会跨域。
同源策略可以很好的保护我们网页不会被随意的篡改,我们的信息不被任意获取,但是在某些情况下也会是我们工作的一个障碍。
比方说,现在公司有两个域名:http://www.example.com 以及 http://www.sample.com。
这两个域名下的页面需要相互进行通信,那么想办法跳过同源限制进行跨域操作,就是我们前端必须掌握的技术。
同源策略对前端功能的影响主要有以下几点:
1. Cookie、Storage、IndexedDB 等无法读取。
2. DOM 无法操作
3. AJAX无法请求。
就目前而言,前端可以实现跨域的技术,主要有以下几点:
A. IFRAME
iframe 的优点体现于,iframe的src属性所连接的资源不受同源策略影响,并且每个iframe都是一个独立的嵌套子窗口。
a. iframe + domain
b. iframe + hash
c. iframe + name
d. iframe + postmessage
B. 协作
这里列举的技术,不在是单纯前端能够实现,而是需要与后端配合。
e. jsonp
f. CROS
g. nginx反向代理
h. WebSocket
C. 其它
i. flash
二、iframe + domain
对于两个域名,如果有相同主域.例如 http://b.example.com 与 http://a.example.com 这两个域名,有相同的主域 example,在这种情况下,我们可以通过指定 domain 来实现两个页面之间的相互通信。
准备:
----------------------------
example.com : A.html
example.com : B.html
在A.html 放入一个iframe 把 B.html 内嵌到 A.html中.
----------------------------
示例:
A.html - Code:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 </head> 7 <body> 8 <p>This is A html</p> 9 <iframe src="http://b.st.cn"></iframe> 10 <button>set B.html BackGround</button> 11 </body> 12 </html> 13 <script> 14 15 var ifr = document.getElementsByTagName(\'iframe\')[0], 16 btn = document.getElementsByTagName(\'button\')[0]; 17 18 document.domain = \'example.com\'; 19 20 load(ifr,function(){ 21 btn.onclick=function(){ 22 ifr.contentWindow.document.body.style.background = \'red\'; 23 } 24 }); 25 26 </script>
B.html - Code:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 </head> 7 <body> 8 <p>This is B html</p> 9 <button>set A.html BackGround</button> 10 </body> 11 </html> 12 <script> 13 var btn = document.getElementsByTagName(\'button\')[0]; 14 15 document.domain = \'example.com\'; 16 17 btn.onclick=function(){ 18 window.top.document.body.style.background = \'blue\'; 19 } 20 </script>
PS : 关于load()方法的说明,见:《Iframe 功能详解》
对于主域相同而子域不同的两个页面,设置domain只可以实现框架之间的交互,如果想进行AJAX请求的话,依然是会有跨域限制。
解决的办法是在要 XMLHttpRequest 请求的目标页面下再增加一个代理 proxy.html。然后设置当前页面与 proxy.html 的 document.domain 。最后再用代理页面去 AJAX 请求目标页面。
示例:
A.html - Code:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 </head> 6 <body> 7 <p>This is A html</p> 8 </body> 9 </html> 10 <script src="jquery.min.js"></script> 11 <script> 12 13 document.domain = \'example.com\'; 14 15 function crossDomainAjax(url,rqurl,callback,id){ 16 17 var ifr = document.createElement(\'iframe\'), 18 rq = function(){ 19 var $ = ifr.contentWindow.$; 20 $.get(rqurl,function(data){ 21 callback && callback($(data)); 22 }); 23 }; 24 25 ifr.src = url; 26 ifr.id = id; 27 28 if(window.attachEvent){ 29 ifr.attachEvent(\'onload\',rq); 30 }else{ 31 ifr.onload= rq; 32 } 33 34 document.body.appendChild(ifr); 35 return false; 36 37 } 38 39 crossDomainAjax(\'http://b.example.com/proxy.html\',\'http://b.example.cn/index.html\',function(data){}); 40 41 </script>
proxy.html - Code :
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 </head> 7 <body> 8 ... 9 </body> 10 </html> 11 <script src="jquery.min.js"></script> 12 <script> 13 document.domain = \'example.com\'; 14 </script>
其中 crossDomainAjax 方法是封装好的用于相同主域,不同子域的AJAX请求。
三、iframe + hash
如果对于域名完全不同的两个页面,便无法再通过设置domain的方式来实现两个页面之间的通信。而使用iframe + hash的组合方式便可以解决不同域之间的页面通信或数据交换,这是因为改变hash并不会导致页面刷新,所以可以利用 location.hash 来进行数据的传递。
iframe+hash实现跨域通信的逻辑是,在example.com域下有一个 A.html,在 sample.com 下有一个B.html,在A.html 中放入一个iframe ,这个 iframe 的 src 指向的是 B.html,并且在 url后面附加url参数,这样A.html 内嵌套 B.html的过程就实现了一个请求的发送,B.html中会去读取发送过来的参数,然后执行相应的处理,处理完成后B.html也会插入一个Iframe,这个iframe嵌入的页面是与A.html同域的代理页面。并将处理的结果附加在代理页面的hash中,最终再由代理页面将处理的结果传递给A.html。
使用iframe+hash来跨域进行数据传递,是有很多弊端的,比如会导致历史记录的产生,数据量限制、内容的直接暴露(URL内容是可见的)、传输的数据类型.... 所以,这里我更加推荐下一种要说到的跨域方式 : iframe + name
具体流程如下图:
准备:
----------------------------
example.com : A.html 、proxy.html
sample.com : B.html
----------------------------
示例:
A.html - Code :
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>A.html</title> 6 </head> 7 <body> 8 <button>SendMessage</button> 9 <script> 10 var obtn = document.getElementsByTagName(\'button\')[0]; 11 12 function handle(ifr) { 13 14 if (window.addEventListener) { 15 window.onhashchange = function() { 16 hash = location.hash.split(\'#\')[1]; 17 if(hash){ 18 console.log(\'接收到了数据:\'+hash); 19 document.body.removeChild(ifr); 20 this.onhashchange=null; 21 location.hash = \'\'; 22 } 23 }; 24 25 } else { 26 27 (function() { 28 var timer = null; 29 setTimeout(function() { 30 hash = location.hash.split(\'#\')[1]; 31 if (hash) { 32 console.log(\'接收到了数据:\'+hash); 33 document.body.removeChild(ifr); 34 timer = null; 35 location.hash = \'\'; 36 return false; 37 } 38 timer = setTimeout(arguments.callee, 1000); 39 }, 0); 40 }()); 41 } 42 document.body.appendChild(ifr); 43 } 44 45 46 obtn.onclick = function() { 47 var ifr = document.createElement(\'iframe\'), 48 iframe = document.getElementById(\'hashCorssDomain\'); 49 50 if(iframe){return false} // 防止并发操作 51 52 ifr.style.display = "none"; 53 ifr.id = \'hashCorssDomain\'; 54 ifr.src = \'htt以上是关于JavaScript 跨域漫游的主要内容,如果未能解决你的问题,请参考以下文章
VSCode自定义代码片段12——JavaScript的Promise对象