代码改变世界

前端跨域

2017-08-02 16:32  库奇——  阅读(226)  评论(0编辑  收藏  举报

同源策略

同源策略是由Netscape提出来的一种广泛应用至今的安全策略,它规定必须符合域名、端口、协议完全相同的页面之间才能够互相访问对方的数据,包括BOM、后端接口等,所有支持JavaScript的浏览器都遵守同源策略,这也是出现跨域问题的原因。

一:利用 JSONP 实现跨域调用

实现原理: <script>元素的src 属性还可以包含来自外部域的JavaScript 文件。

JSONP 是 JSON 的一种使用模式,可以解决主流浏览器的跨域数据访问问题。其原理是根据 XmlHttpRequest 对象受到同源策略的影响,而 <script> 标签元素却不受同源策略影响,可以加载跨域服务器上的脚本,网页可以从其他来源动态产生 JSON 资料。用 JSONP 获取的不是 JSON 数据,而是可以直接运行的 JavaScript 语句。 

JSONP协议建立在网页允许跨域加载JavaScript脚本文件的前提下,具体步骤是:

  • 请求发起方在页面添加一个script标签,地址是另一个域的接口地址
  • 请求发起方通过接口地址的URL查询字符串传递参数,包括约定好的回调函数名称(比如callback)
  • 响应方收到请求并且处理数据,生成一段调用回调函数的JavaScript代码并返回
  • script标签加载到JavaScript代码后自动执行回调函数,将返回的数据传递给回调函数作为参数

比如:

http://www.domain.com/a.html页面添加一个标签

<script src=“http://test.domain.com/b.php?callback=fun1&id=1”></script>

http://test.domain.com/b.php接收请求并生成JavaScript代码返回。

fun(

  {code: 0,data: 1234}

);

下面是一个简单的请求百度接口的jsonp

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>jsonp</title>
</head>
<body>
    <div style="text-align: center;">
     <input type="text" id="text" >
 </div>
 <ul id="listBox">
 </ul>
 <script>
  /* JSONP协议建立在网页允许跨域加载JavaScript脚本文件的前提下,具体步骤是:
    请求发起方在页面添加一个script标签,地址是另一个域的接口地址
    请求发起方通过接口地址的URL查询字符串传递参数,包括约定好的回调函数名称(比如callback)
    响应方收到请求并且处理数据,生成一段调用回调函数的JavaScript代码并返回
    script标签加载到JavaScript代码后自动执行回调函数,将返回的数据传递给回调函数作为参数*/
    text.oninput=function(ev){
        if(!scrEle){
            var scrEle = document.createElement("script");
            scrEle.id = "scrEle";
            scrEle.src = "https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?cb=callback&wd="+ev.target.value//wd为查询参数 cb为回掉函数
            document.getElementsByTagName("head")[0].appendChild(scrEle);
        }else{
          document.body.removeChild(scrEle); 
      }  
  }
  function callback(json) {
     listBox.innerHTML = '';
     for(var i = 0; i < json.s.length; i ++) {
        listBox.innerHTML += '<li>'+json.s[i]+'</li>'
    }
}
</script>
</body>
</html>

 

优点:1使用简单 2 兼容性好 3

缺点:1它只支持GET请求而不支持POST等其它类型的HTTP请求 2 jsonp在调用失败的时候不会返回各种HTTP状态码。 3 安全性


 

二 :使用 CORS 实现跨域调用

CORS(Cross-Origin Resource Sharing,跨源资源共享)是W3C 的一个工作草案,定义了在必须访问跨源资源时,浏览器与服务器应该如何沟通。CORS 背后的基本思想,就是使用自定义的HTTP 头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。

对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

服务器端对于CORS的支持,主要就是通过设置Access-Control-Allow-Origin来进行的。

优点:

  • JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。
  • 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。

缺点:

  • 需要服务器的支持
  • JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS)。

三:图像Ping

我们知道,一个网页可以从任何网页中加载图像,不用担心跨域不跨域。这也是在线广告跟踪浏览量的主要方式。也可以动态地创图像,使用它们的onload 和onerror 事件处理程序来确定是否接收到了响应。

动态创建图像经常用于图像Ping。图像Ping 是与服务器进行简单、单向的跨域通信的一种方式。请求的数据是通过查询字符串形式发送的,而响应可以是任意内容,但通常是像素图或204 响应。通过图像Ping,浏览器得不到任何具体的数据,但通过侦听load 和error 事件,它能知道响应是什么时候接收到的。

来看下面的例子。

var img = new Image();
img.onload = img.onerror = function(){
alert("Done!");
};
img.src = "http://www.example.com/test?name=Nicholas";这里创建了一个Image 的实例,然后将onload 和onerror 事件处理程序指定为同一个函数。这样无论是什么响应,只要请求完成,就能得到通知。请求从设置src 属性那一刻开始,而这个例子在请求中发送了一个name 参数。图像Ping 最常用于跟踪用户点击页面或动态广告曝光次数。

图像Ping 有两个主要的缺点,

  • 一是只能发送GET 请求,
  • 二是无法访问服务器的响应文本。

因此,图像Ping 只能用于浏览器与服务器间的单向通信。.

四:使用web socket

使用web sockets是一种浏览器的API,它的目标是在一个单独的持久连接上提供全双工、双向通信。(同源策略对web sockets不适用)

web sockets原理:在JS创建了web socket之后,会有一个HTTP请求发送到浏览器以发起连接。取得服务器响应后,建立的连接会使用HTTP升级从HTTP协议交换为web sockt协议。

只有在支持web socket协议的服务器上才能正常工作。

var socket = new WebSockt('ws://www.baidu.com');//http->ws; https->wss

socket.send('hello WebSockt');

socket.onmessage = function(event){
  var data = event.data;
}


 

五:跨域页面数据通讯

1:不同主域的页面

HTML5提供了postMessage/sendMessage等API允许从一个域发送消息到指定的域,另一个域下的网页可以通过onmessage监听消息并且做出处理。跨文档消息传送,有时候简称为XDM,指的是在来自不同域的页面间传递消息。

postMessage(data,origin)方法接受两个参数

 1.data:要传递的数据,html5规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify()方法对对象参数序列化,在低版本IE中引用json2.js可以实现类似效果。

2.origin:字符串参数,指明目标窗口的源,协议+主机+端口号[+URL],URL会被忽略,所以可以不写,这个参数是为了安全考虑,postMessage()方法只会将message传递给指定窗口,当然如果愿意也可以建参数设置为"*",这样可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。

1:http://wowo.yinker.com

var iframe = document.getElementById('iframe');
      iframe.onload = function() {
          var data = {
              name: 'aym'
          };
          // 向domain2传送跨域数据
        iframe.contentWindow.postMessage(JSON.stringify(data), 'http://xc.yinker.com/page/aboutUs.do'); 
     }; 

      // 接受domain2返回数据 w
      window.addEventListener('message', function(e) { 
          console.log(e.data); 
      }, false); 

2:http://xc.yinker.com

window.addEventListener('message', function(event){
  console.log(event.origin)
  if (event.origin == 'http://wowo.yinker.com') {
    console.log(event.data); //name: 'aym'
    window.parent.postMessage("另一个域名发送过来的", 'http://wowo.yinker.com');
  }
}, false)

2: 同主域嵌套页面  document.domain + iframe跨域

修改domain为主域,比如a.domain.com修改为domain.com,即可互相访问浏览器对象模型。

1.)父窗口:http://wowo.yinker.com/page/aboutUs.do

<iframe id="iframe" src="http://xc.yinker.com/page/aboutUs.do"></iframe>
<script>
    document.domain = 'yinker.com';
    var user = 'admin';
  console.log(document.getElementById('iframe').contentWindow.child)//获取子ifarme的变量
</script>

2.)子窗口:http://xc.yinker.com/page/aboutUs.do

<script>
    document.domain = yinker.com';
  var child = "child"
    // 获取父窗口中变量
    coonsole.log(window.parent.user);
</script>

3:不同主域嵌套页面

修改子页面的iframe的hash值