[跨域]前端解决跨域问题

1.同源策略以下:

URL 说明 是否容许通讯
http://www.a.com/a.js
http://www.a.com/b.js
同一域名下 容许
http://www.a.com/lab/a.js
http://www.a.com/script/b.js
同一域名下不一样文件夹 容许
http://www.a.com:8000/a.js
http://www.a.com/b.js
同一域名,不一样端口 不容许
http://www.a.com/a.js
https://www.a.com/b.js
同一域名,不一样协议 不容许
http://www.a.com/a.js
http://70.32.92.74/b.js
域名和域名对应ip 不容许
http://www.a.com/a.js
http://script.a.com/b.js
主域相同,子域不一样 不容许
http://www.a.com/a.js
http://a.com/b.js
同一域名,不一样二级域名(同上) 不容许(cookie这种状况下也不容许访问)
http://www.cnblogs.com/a.js
http://www.a.com/b.js
不一样域名 不容许
特别注意两点:
第一,若是是协议和端口形成的跨域问题“前台”是无能为力的,
第二:在跨域问题上,域仅仅是经过“URL的首部”来识别而不会去尝试判断相同的ip地址对应着两个域或两个域是否在同一个ip上。
“URL的首部”指window.location.protocol +window.location.host,也能够理解为“Domains, protocols and ports must match”。

 浏览器都有一个同源策略,其限制之一就是第一种方法中咱们说的不能经过ajax的方法去请求不一样源中的文档。 它的第二个限制是浏览器中不一样域的框架之间是不能进行js的交互操做的。javascript

 

 

2前端解决跨域问题

如下的例子包含的文件均为为 http://www.a.com/a.html http://www.a.com/proxy.html 与 http://www.b.com/b.html,要作的都是从a.html获取b.html里的数据php

一 JSONP

 原理:jsonp是利用script标签没有跨域限制的特性,经过在srcurl的参数上附加回调函数名字,而后服务器接收回调函数名字并返回一个包含数据的回调函数html

         经过script标签引入一个js文件,这个js文件载入成功后会执行咱们在url参数中指定的函数,而且会把咱们须要的json数据做为参数传入。因此jsonp是须要服务器端的页面进行相应的配合的。前端

 示例:html5

    function doSomething(data) {
        // 对data处理
    }
    var script = document.createElement("script");
    script.src = "http://www.b.com/b.html?callback=doSomething";
    document.body.appendChild(script);

    // 1.生成一个script标签,将其append在body上,向服务器发出请求
    // 2.服务器根据 callback 这个参数生成一个包含数据的函数 doSomething({"a", "1"})
    // 3.页面事先已声明doSomething函数,此时执行 doSomething(data) 这个函数,得到数据

 

二 window.name + iframe

原理:java

window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的全部的页面都是共享一个window.name的,每一个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的全部页面中的,并不会因新页面的载入而进行重置。web

window.name 的美妙之处:name 值在不一样的页面(甚至不一样域名)加载后依旧存在,而且能够支持很是长的 name 值(2MB)。ajax

window.name的原理是利用同一个窗口在不一样的页面共用一个window.name,这个须要在a.com下创建一个代理文件proxy.html,使同源后a.html能获取proxy.htmlwindow.namejson

name 在浏览器环境中是一个全局/window对象的属性,且当在 frame 中加载新页面时,name 的属性值依旧保持不变。经过在 iframe 中加载一个资源,该目标页面将设置 frame 的 name 属性。此 name 属性值可被获取到,以访问 Web 服务发送的信息。但 name 属性仅对相同域名的 frame 可访问。这意味着为了访问 name 属性,当远程 Web 服务页面被加载后,必须导航 frame 回到原始域。同源策略依旧防止其余 frame 访问 name 属性。一旦 name 属性得到,销毁 frame 。 segmentfault

示例:

a.com/a.html

  var iframe = document.createElement("iframe");
    iframe.src = "http://www.b.com/b.html";
    document.body.appendChild(iframe); // 如今a.html里建一个引用b.html的iframe,得到b的数据

    var flag = true;
    iframe.onload = function() {
        if (flag) {
            iframe.src = "proxy.html";             
   // 判断是第一次载入的话,这时候iframe的window.name就含有所但愿得到的数据,这时候设置iframe和a.html在同源,才能获取iframe的window.name的值,改变一个iframe的地址(src属性)从新加载后触发onload事件,第二次载入进入时flag为false进入else循环
            flag = false;
        } else { // 第二次载入因为a和proxy同源,a能够直接获取proxy的window.name
            alert(iframe.contentWindow.name);

            iframe.contentWindow.close();
            document.body.removeChild(iframe);
            iframe.src = '';
            iframe = null;
        }
    }

b.com/b.html

window.name = "这是 b 页面的数据";

Web 服务器如何提供 window.name 数据

为了让 Web 服务器实现 window.name,服务器应该只寻找请求中是否包含 windowname 参数。若是包含了 windowname 参数,服务器应该返回一个设置了 window.name 字符串值的 HTML 文档,回应此请求并传送到客户端。例如:
http://www.a.com/getdata.html?windowname=true

若是服务器想用 Hello 响应客服端,它应该返回一个 HTML 页面:

<html>     <script type="text/javascript">         window.name="Hello";     </script> </html>

一样也能够转换为 JSON 数据:

<html>     <script type="text/javascript">         window.name='{"foo":"bar"}';     </script> </html>

  

 

三 document.domain(同属于同一个基础域名的两个域名之间可用)

原理:

两个属于同一个基础域名的域名的 document.domain设置成自身或更高一级的父域,使得document.domain相同,这样经过在页面中书写js代码来获取iframe中的东西

 

浏览器都有一个同源策略,其限制之一就是第一种方法中咱们说的不能经过ajax的方法去请求不一样源中的文档。 它的第二个限制是浏览器中不一样域的框架之间是不能进行js的交互操做的。有一点须要说明,不一样的框架之间(父子或同辈),是可以获取到彼此的window对象的,但蛋疼的是你却不能使用获取到的window对象的属性和方法(html5中的postMessage方法是一个例外,还有些浏览器好比ie6也可使用top、parent等少数几个属性),总之,你能够当作是只能获取到一个几乎无用的window对象。好比,有一个页面,它的地址是http://www.example.com/a.html  , 在这个页面里面有一个iframe,它的src是http://example.com/b.html, 很显然,这个页面与它里面的iframe框架是不一样域的,因此咱们是没法经过在页面中书写js代码来获取iframe中的东西

利用document.domain 实现跨域:
前提条件:这两个域名必须属于同一个基础域名!并且所用的协议,端口都要一致,不然没法利用document.domain进行跨域

 

有另外一种状况,两个子域名:

aaa.xxx.com

bbb.xxx.com

aaa里的一个网页(a.html)引入了bbb 里的一个网页(b.html),这时a.html里一样是不能操做b.html里面的内容的。由于document.domain不同,一个是aaa.xxx.com,另外一个是bbb.xxx.com。这时咱们就能够经过Javascript,将两个页面的domain改为同样的,须要在a.html里与b.html里都加入:document.domain = "xxx.com";这样这两个页面就能够互相操做了。也就是实现了同一基础域名之间的"跨域"。

示例:

 

a.com/a.html

  document.domain = "a.com";
    var iframe = document.createElement("iframe");
    iframe.src = "http://b.a.com/b.html";
    document.body.appendChild(iframe);
    iframe.onload = function() {
        console.log(iframe.contentWindow....); // 在这里操做b.html里的元素数据 这样咱们就能够经过js访问到iframe中的各类属性和对象了。
    }

 b.a.com/b.html

document.domain = "a.com";

注意:document.domain须要设置成自身或更高一级的父域,且主域必须相同。

 不过若是你想在http://www.example.com/a.html 页面中经过ajax直接请求http://example.com/b.html 页面,即便你设置了相同的document.domain也仍是不行的,因此修改document.domain的方法只适用于不一样子域的框架间的交互。若是你想经过ajax的方法去与不一样子域的页面交互,除了使用jsonp的方法外,还能够用一个隐藏的iframe来作一个代理。原理就是让这个iframe载入一个与你想要经过ajax获取数据的目标页面处在相同的域的页面,因此这个iframe中的页面是能够正常使用ajax去获取你要的数据的,而后就是经过咱们刚刚讲得修改document.domain的方法,让咱们能经过js彻底控制这个iframe,这样咱们就可让iframe去发送ajax请求,而后收到的数据咱们也能够得到了。

 

 

四 HTML5的postMessage

原理:

 

假设a.html里嵌套个<iframe src="http://www.b.com/b.html" frameborder="0"></iframe>,在这两个页面里互相通讯

 示例:

 a.com/a.html

 window.onload = function() {
        window.addEventListener("message", function(e) {
            alert(e.data);
        });
      //  window.frames[0].postMessage("b data", "http://www.b.com/b.html");  
    }

 b.com/b.html

window.onload = function() {
   //      window.addEventListener("message", function(e) {
  //          alert(e.data);
 //       });
        window.parent.postMessage("a须要的数据", "http://www.a.com/a.html");
    }

 

 

五 CORS

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)克服了AJAX只能同源使用的限制

CORS背后的思想,就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,仍是应该失败。

 

CORSXMLHttpRequest Level 2 里规定的一种跨域方式。在支持这个方式的浏览器里,javascript的写法和不跨域的ajax写法如出一辙,

只要服务器须要设置Access-Control-Allow-Origin: * 或者请求头中Origin的源信息

(ajax跨域请求时,会自动在请求头部添加一个 Origin: http://www.ccd.com 协议 域名 端口 以便于服务器根据这个头部信息来决定是否给予响应,因此关键在于服务器的设置)

若是没有这个头部,或者有这个头部可是源信息不匹配,浏览器就驳回请求

 

六 websocket

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;
}

 

七 window.location.hash + iframe

原理:

 b.html将数据以hash值的方式附加到proxy.htmlurl上,在proxy.html页面经过location.hash获取数据后传到a.html(这个例子是传到a.htmlhash上,固然也能够传到其余地方)

 

示例:

a.com/a.html

 var iframe = document.createElement("iframe");
    iframe.src = "http://www.b.com/b.html"; document.body.appendChild(iframe); // 在a页面引用b function check() { // 设置个定时器不断监控hash的变化,hash一变说明数据传过来了 var hashs = window.location.hash; if (hashs) { clearInterval(time); alert(hashs.substring(1)); } } var time = setInterval(check, 30);

 

b.com/b.html

  window.onload = function() {
        var data = "this is b's data"; var iframe = document.createElement("iframe"); iframe.src = "http://www.a.com/proxy.html#" + data; document.body.appendChild(iframe); // 将数据附加在proxy.html的hash上 }

 

a.com/proxy.html

// 获取自身的hash再传到a.html的hash里,数据传输完毕
parent.parent.location.hash = self.location.hash.substring(1); 

 

八 经过script标签加载

对于浏览器来讲,script标签的src属性所指向资源就跟img标签的src属性所指向的资源同样,都是一个静态资源,浏览器会在适当的时候自 动去加 载这些资源,而不会出现所谓的跨域问题。这样咱们就能够经过该属性将要访问的数据对象引用进当前页面而绕过js跨域问题。 例如,须要在 hi域 下管理中心页面中随机推荐几个热门模块给用户,因为热门模块的相关信息都在 act域 下的php模块中维 护,若是直接在hi域下经过ajax请求去获取act域下的推荐模块列表相关信息就出现js跨域问题。解决这个问题的最简单方法就是,在hi域下经过 script标签去访问act域提供的这个http接口:

<script type=”text/javascript” src=”http://act.hi.baidu.com/widget/recommend”><script>

 

参考:https://segmentfault.com/a/1190000003882126

   http://www.cnblogs.com/JChen666/p/3399951.html