web跨域解决方案

阅读目录javascript

摘要:跨域问题,不管是面试仍是平时的工做中,都会遇到,本文总结处理跨域问题的几种方法以及其原理,也让本身搞懂这方面的知识,走起。php

 

什么是跨域

  

    在JavaScript中,有一个很重要的安全性限制,被称为“Same-Origin Policy”(同源策略)。这一策略对于JavaScript代码可以访问的页面内容作了很重要的限制,即JavaScript只能访问与包含它的文档在同一域下的内容。css

  JavaScript这个安全策略在进行多iframe或多窗口编程、以及Ajax编程时显得尤其重要。根据这个策略,在baidu.com下的页面中包含的JavaScript代码,不能访问在google.com域名下的页面内容;甚至不一样的子域名之间的页面也不能经过JavaScript代码互相访问。对于Ajax的影响在于,经过XMLHttpRequest实现的Ajax请求,不能向不一样的域提交请求,例如,在abc.example.com下的页面,不能向def.example.com提交Ajax请求,等等。html

  为何浏览器要实现同源限制?咱们举例说明:前端

  好比一个黑客,他利用iframe把真正的银行登陆页面嵌到他的页面上,当你使用真实的用户名和密码登陆时,若是没有同源限制,他的页面就能够经过javascript读取到你的表单中输入的内容,这样用户名和密码就轻松到手了.html5

  又好比你登陆了OSC,同时浏览了恶意网站,若是没有同源限制,该恶意 网站就能够构造AJAX请求频繁在OSC发广告帖.java

 

跨域的状况分为如下几种:jquery

  

  特别注意两点:面试

  一、若是是协议和端口形成的跨域问题“前台”是无能为力的ajax

  二、在跨域问题上,域仅仅是经过“URL的首部”来识别而不会去尝试判断相同的ip地址对应着两个域或两个域是否在同一个ip上。好比上面的,http://www.a.com/a.js和http://70.32.92.74/b.js。虽然域名和域名的ip对应,不过仍是被认为是跨域。

“URL的首部”指window.location.protocol +window.location.host。其中,

  window.location.protocol:指含有URL第一部分的字符串,如http:

    window.location.host:指包含有URL中主机名:端口号部分的字符串.如//www.cenpok.net/server/

 

经常使用的几种跨域处理方法:

 

 一、JSONP

 二、CORS策略

 三、document.domain+iframe的设置

 四、HTML5的postMessage

 五、使用window.name来进行跨域

 

跨域的原理解析及实现方法

 

一、JSONP(JSON with padding)

原理 :

      咱们知道,在页面上有三种资源是能够与页面自己不一样源的。它们是:js脚本,css样式文件,图片,像淘宝等大型网站,确定会将这些静态资源放入cdn中,而后在页面上连

接,以下所示,因此它们是能够连接访问到不一样源的资源的。

1)<script type="text/javascript" src="某某cdn地址" ></script>

2)<link type="text/css" rel="stylesheet" href="某个cdn地址" />

3)<img src="某个cdn地址" alt=""/>

  而jsonp就是利用了script标签的src属性是没有跨域的限制的,从而达到跨域访问的目的。所以它的最基本原理就是:动态添加一个<script>标签来实现。

 

实现方法:

    这里是使用ajax来请求的,看起来和ajax没啥区别,其实仍是有区别的。

    ajax的核心是经过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加<script>标签来调用服务器提供的js脚本。

复制代码
$.ajax({  
        url:"http://crossdomain.com/services.php",  
        dataType:'jsonp',  
        data:'',  
        jsonp:'callback',  
        success:function(result) {  
            // some code
        }  
    });  
复制代码

上面的代码中,callback是必须的,callback是什么值要跟后台拿。获取到的jsonp数据格式以下:

flightHandler({
    "code": "CA1998",
    "price": 1780,
    "tickets": 5
});

jsonp的全称为json with padding,上面的数据中,flightHandler就是那个padding.

 

 JSONP的不足之处:

  一、只能使用get方法,不能使用post方法:

  咱们知道 script,link, img 等等标签引入外部资源,都是 get 请求的,那么就决定了 jsonp 必定是 get 的。但有时候咱们使用的 post 请求也成功,为啥呢?这是由于当咱们指定dataType:'jsonp',不论你指定:type:"post" 或者type:"get",其实质上进行的都是 get 请求!

  二、没有关于 JSONP 调用的错误处理。若是动态脚本插入有效,就执行调用;若是无效,就静默失败。失败是没有任何提示的。例如,不能从服务器捕捉到 404 错误,也不能取消或从新开始请求。不过,等待一段时间尚未响应的话,就不用理它了。

 

 二、CORS策略

 原理:

     CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它容许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。它为Web服务器定义了一种方式,容许网页从不一样的域访问其资源.

  CORS系统定义了一种浏览器和服务器交互的方式来肯定是否容许跨域请求。 它是一个妥协,有更大的灵活性,但比起简单地容许全部这些的要求来讲更加安全。

 

实现方法:

  CORS须要浏览器和服务器同时支持。目前,全部浏览器都支持该功能,IE浏览器不能低于IE10。

  整个CORS通讯过程,都是浏览器自动完成,不须要用户参与。对于开发者来讲,CORS通讯与同源的AJAX通讯没有差异,代码彻底同样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感受。

   

前端方面

之前咱们使用Ajax,代码相似于以下的方式:

var xhr = new XMLHttpRequest(); 
xhr.open("GET", "/hfahe", true); 
xhr.send(); 
// 这里的“/hfahe”是本域的相对路径。

若是咱们要使用CORS,相关Ajax代码可能以下所示:

var xhr = new XMLHttpRequest(); 
xhr.open("GET", "http://blog.csdn.net/hfahe", true); 
xhr.send(); 
// 请注意,代码与以前的区别就在于相对路径换成了其余域的绝对路径,也就是你要跨域访问的接口地址。

 

服务器方面
服务器端对于CORS的支持,主要就是经过设置Access-Control-Allow-Origin来进行的。若是浏览器检测到相应的设置,就能够容许Ajax进行跨域的访问。

 

CORS策略的优缺点:

  优势:

    一、CORS支持全部类型的HTTP请求。

    二、 使用CORS,开发者可使用普通的XMLHttpRequest发起请求和得到数据,比起JSONP有更好的错误处理。

  缺点: 兼容性方面相对差一点,ie10或以上才支持

 

三、document.domain+iframe的设置  (只有在主域相同的时候才能使用该方法)

原理:

  浏览器中不一样域的框架之间是不能进行js的交互操做的。可是不一样的框架之间(父子或同辈),是可以获取到彼此的window对象的,可是,咱们也只能获取到一个几乎

无用的window对象。好比,有一个页面,它的地址是 http://www.example.com/a.html , 在这个页面里面有一个iframe,它的src是 http://example.com/b.html , 很显然,这

个页面与它里面的iframe框架是不一样域的,因此咱们是没法经过在页面中书写js代码来获取iframe中的东西的。

  这个时候,document.domain就能够派上用场了,咱们只要把 http://www.example.com/a.html 和  http://example.com/b.html 这两个页面的document.domain都设成

相同的域名就能够了。但要注意的是,document.domain的设置是有限制的,咱们只能把document.domain设置成自身或更高一级的父域,且主域必须相同。例如:

a.b.example.com 中某个文档的document.domain 能够设成a.b.example.com、b.example.com 、example.com中的任意一个,可是不能够设成 c.a.b.example.com,由于这是

当前域的子域,也不能够设成baidu.com,由于主域已经不相同了。

 

使用方法:

  好比在http://www.example.com/a.html 的页面里要访问 http://example.com/b.html里面的东西。

  在页面 http://www.example.com/a.html 中设置document.domain:

复制代码
//http://www.example.com/a.html
<html>
<head>
    <title>A页面</title>
    <script type="text/javascript" src="jquery.js"></script>
</head>
<body>
    <div>A页面</div>
    <iframe id="iframe" src="http://example.com/b.html" style="display:none;"></iframe>
  // 至关于用一个隐藏的iframe来作代理 <script> $(function(){ try{ document.domain = "example.com"; //这里将document.domain设置成同样 }catch(e){} $("#iframe").load(function(){ var iframe = $("#iframe").contentDocument.$; ifram.get("http://example.com/接口",function(data){}); }); }); </script> <body> </html>
复制代码

 

在页面 http://example.com/b.html 中也设置document.domain,并且这也是必须的,虽然这个文档的domain就是example.com,可是仍是必须显示的设置document.domain的值:

复制代码
//http://example.com/b.html
<html>
<head>
    <title>B页面</title>
    <script type="text/javascript" src="jquery.js"></script>
</head>
<body>
    <div>B页面</div>
    <script>        
        $(function(){
            try{
            document.domain = "example.com"; //这里将document.domain设置成同样
            }catch(e){}
        });
    </script>
</body>
</html>
复制代码

  这里有个注意点,就是在A页面中,要等iframe标签完成加载B页面以后,再取iframe对象的contentDocument,不然若是B页面没有被iframe彻底加载,在A页面中经过contentDocument属性就取不到B页面中的jQuery对象。

      一旦取到B页面中的jQuery对象,就能够直接发ajax请求了,这种相似“代理”方式能够解决主子域的跨域问题。

缺点:

  只有在主域相同的时候才能使用该方法

四、HTML5的postMessage

原理:

  没啥原理,就是一个html5所提供的一个API.--->HTML5 window.postMessage是一个安全的、基于事件的消息API。

 

  在须要发送消息的源窗口调用postMessage方法便可发送消息。其中.源窗口能够是全局的window对象,也能够是如下类型的窗口:

  一、文档窗口中的iframe:

var iframe = document.getElementById('my-iframe');
var win = iframe.documentWindow;

  二、JavaScript打开的弹窗:

var win = window.open();

  三、当前文档窗口的父窗口:

var win = window.parent;

  四、

var win = window.opener();

 

      发送消息:找到源window对象后,便可调用postMessage API向目标窗口发送消息:

win.postMessage(msg, targetOrigin);

说明:postMessage函数接收两个参数:

  一、msg, 将要发送的消息,可使一切javascript参数,如字符串,数字,对象,数组等。

  二、targetOrigin,这个参数称做“目标域”,注意,是目标域不是本域!好比,你想在2.com的网页上往1.com网页上传消息,那么这个参数就是“http://1.com/”,而不是2.com.协议,(一个完整的域名包括:主机名,端口号。如:http://g.cn:80/)

 

    接收消息:那目标窗口要怎么接收传过来的数据呢,只要监听window的message事件就能够接收了。

复制代码
var onmessage = function (event) {
    var data = event.data;
    var origin = event.origin;
    //do someing
};
if (typeof window.addEventListener != 'undefined') {
    window.addEventListener('message', onmessage, false);
} else if (typeof window.attachEvent != 'undefined') {
    //for ie
    window.attachEvent('onmessage', onmessage);
}
复制代码

message事件监听函数接收一个参数,Event对象实例,该对象有三个属性:

  • data : 消息
  • origin:消息的来源地址
  • source:发送消息窗口的window对象引用

 


使用方法(案例):

  http://test.com/index.html--> 发送消息的页面

复制代码
<!-- 这个是 http://test.com/index.html 页面 -->
<div> <!-- 要给下面的页面传一个妹子过去 --> <iframe id="child" src="http://lsLib.com/lsLib.html"></iframe> </div> <script type="text/javascript"> window.onload=function(){ window.frames[0].postMessage('苍老师','http://lslib.com'); } </script>
复制代码

 

http://lslib.com/lslib.html --> 接收消息的页面

复制代码
<!-- 这个是 http://lslib.com/lslib.html 页面 -->
<script type="text/javascript">
    window.addEventListener('message',function  (e) {
        console.log(e.origin,e.data);
        alert('收到妹子一枚:'+e.data);
    });
</script>
复制代码

 

优缺点:

 优势:方便,安全,有效的解决了跨域问题

 缺点:万恶的资本主义,ie8+才支持,并且ie8+<ie10只支持iframe的方式。

 

 五、使用window.name来进行跨域(相对比较完美的方法)

 原理:  

  当iframe的页面跳到其余地址时,其window.name值保持不变,而且能够支持很是长的 name 值(2MB)。

  浏览器跨域iframe禁止互相调用/传值.可是调用iframe时 window.name 却不变,正是利用这个特性来互相传值,固然跨域下是不允许读取ifram的window.name值.

       因此这里咱们还要准备一个和主页面http://www.a.com/main.html 相同域下的代理页面http://www.a.com/other.html ,iframe调用子页面 http://www.b.com/data.html

 

使用方法:

 一、 准备三个页面:

  http://www.a.com/main.html   //应用页面

  http://www.a.com/other.html    // 代理页面,要求和应用页面在同一个域。通常是一个空的html

      http://www.b.com/data.html   //应用页面获取数据的页面,简称:数据页面

    二、

     数据页面将数据传到window.name中去。以下:

     http://www.b.com/data.html中的 data.html

// data.html
window.name="苍老师"; //能够是其余类型的数据,好比数组,对象等等

   

    http://www.a.com/main.html   //应用页面的代码以下:

复制代码
<!-- main.html -->
var iframeData;

var state = 0;//开关变量

var iframe = document.createElement('iframe'); //建立iframe

var loadfn = function() {

    if (state === 1) {

        iframeData = iframe.contentWindow.name;  // 读取数据

        alert('获取到了iframe传过来的妹子'+iframeData);

    }else if (state === 0) {

         state = 1;
         iframe.contentWindow.location = 'http://www.a.com/other.html';  //这里是代理页面 other.html
         
         /**
             这里说明一下:
             因为iframe的location改变了,至关于从新载入页面(这是iframe的性质决定的),因而从新执行loadfn方法。
        因为当iframe的页面跳到其余地址时,其window.name值保持不变,而且此时开关变量 state已经变为1,
因而就能够获取到window.name值,也就达到了跨域访问的目的了。 **/ }; } iframe.src = 'http://www.b.com/data.html'; //这是是数据页面,data.html if (iframe.attachEvent) { iframe.attachEvent('onload', loadfn); } else { iframe.onload = loadfn; } document.body.appendChild(iframe);
复制代码

 

三、获取数据之后销毁这个iframe,释放内存;这也保证了安全(不被其余域frame js访问)。

    iframe.contentWindow.document.write('');
    iframe.contentWindow.close();
    document.body.removeChild(iframe);

 

优缺点:

  浏览器支持状况好,是比较广泛的使用方法

 

 

总结

 

以上总结了js跨域的几种方法,固然还有其余的方法,不过没有。他们各有千秋。其实最主要的区别除了实现方式不同,主要是浏览器的兼容问题而已。

 

JSONP:

       JSONP的优势是:它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中均可以运行,不须要XMLHttpRequest或ActiveX的支持;而且在请求完毕后能够经过调用callback的方式回传结果。

      JSONP的缺点则是:它只支持GET请求而不支持POST等其它类型的HTTP请求;它只支持跨域HTTP请求这种状况,不能解决不一样域的两个页面之间如何进行JavaScript调用的问题。

 

CORS策略

优势:使用CORS,开发者可使用普通的XMLHttpRequest发起请求和得到数据,比起JSONP有更好的错误处理。

 

     缺点:古老的浏览器不支持,不过大部分现代浏览器都支持

 

document.domain+iframe:只适用于主域相同的跨域问题处理

 

html5的postMessage:

  优势html5新引进的特性,可使用它来向其它的window对象发送消息,不管这个window对象是属于同源或不一样源,目前IE8+、FireFox、Chrome、Opera等浏览器都已经支持window.postMessage方法。若是是现代浏览器,首选。

  缺点: ie8之前不支持

 

window.name:

  主要是应用当frame的页面跳到其余地址时,其window.name值保持不变的原理。兼容性好。须要照顾落后的浏览器时,首选。、

 

小小总结,有误之处,欢迎指出

 

若是您以为文章有用,也能够给咸鱼老弟发个微信小额红包鼓励,

相关文章
相关标签/搜索