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对象

VSCode自定义代码片段12——JavaScript的Promise对象

30秒就能看懂的JavaScript 代码片段

Javascript跨域

常用Javascript代码片段集锦

48个值得掌握的JavaScript代码片段(上)