浏览器的跨域问题以及解决方案

1、为什么会有跨域问题的存在?

   JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象,即同源政策。


2、什么是同源?

   1995年,同源政策由 Netscape 公司引入浏览器。目前,所有浏览器都实行这个政策。
  最初,它的含义是指,A网页设置的 Cookie,B网页不能打开,除非这两个网页"同源"。所谓"同源"指的是"三个相同"。

  (1)协议相同

  (2)域名相同

  (3)端口相同

具体实例  比如:http://www.example.com/zw/index.html这个网站,它的协议是http://,域名是www.example.com,端口号是80(默认端口可以省略),它的同源情况如下:

   ①、http://www.example.com/zwxk/manager.html    同源

   ②、https://www.example.com/zw/index.html   不同源(协议不同)

   ③、http://examle.com/zw/index.html  不同源(域名不同)

   ④、http://www.example.com:81zw/index.html   不同源(端口号不同)


3、同源政策的目的

同源策略的目的是为了保证用户信息的安全。防止恶意的网站盗取数据。

设想这样一个情景:A网站是一家银行,用户登录以后,又去浏览其他的网站B,如果网站B可以读取A网站的Cookie,会发生什么问题?

显然,如果Cookie包含隐私(比如存款总额),这些信息就会泄露,更可怕的是,Cookie往往用来保存用户的登录状态,如果用户没有退出登录,其他网站就可以冒用户,为所欲为。因为浏览器同时还规定,提交表单不受同源策略的限制。

由此可见,“同源政策”的必要性,否则Cookie可以共享,互联网就毫无安全可言了。


3、非同源限制范围

    随着互联网的发展,“同源政策”越来越严格。目前,如果非同源,共有三种行为受到限制。

  (1)Cookie、LocalStorage和IndexDB无法获取。

  (2)DOM无法获得。

  (3)AJAX请求不能发送。

虽然这些限制是必要的,但是有时很不方便,合理的用途也受到影响。下面将介绍如何规避上面三种限制。


4-1、解决跨域一:Cookie如何实现跨域

    Cookie是服务器写入浏览器的一段信息,只有同源的网页才能共享,但是,两个网页一级域名相同,只是二级域名不同,浏览器允许通过设置document.domain共Cookie。

举例来说,A网站是:http:weibo.qq.com

  B网站是:http:lol.qq.com


那么只需设置相同的document.domain,两个网页就可共享Cookie。

document.domain = 'qq.com';

现在,A网页通过脚本设置一个Cookie。

document.Cookie = "test1=hello";

B网页就能到这个Cookie。

var getCookie = document.cookie;

注意:这种方法只适用于Cookie和iframe窗口,LocalStorage和IndexDB无法通过这种方法规避,而要使用下文将介绍的PostMessage API。

另外,服务器也可以在设置Cookie的时候,指定Cookie的所属域名为一级域名,比如:.qq.com

Set-Cookie:key=value;domain=.qq.com;path=/

这样的话,二级域名和三级域名不用做任何设置,都可以读取这个Cookie。


4-2、解决跨域问题二:如何跨域获取DOM。

如果两个网页不同源,就无法拿到对方的DOM。典型的例子是iframe窗口和window.open方法打开的窗口,它们与父窗口无法通信。

   比如,父窗口运行下面的命令,如果iframe窗口不是同源将会报错。

document.getElementById("iframe").contentWindow.document

   上面命令中,父窗口想获取子窗口的DOM,应为跨源导致报错。

   反之亦然,子窗口获取主窗口的DOM也会报错。

window.parent.document.body

   如果两个窗口一级域名相同,只是二级域名不同,那么设置4-1介绍的document.domain属性,就可规避同源政策,拿到DOM。

   对于完全不相同的网站,目前有三种方法,可以解决跨域窗口的通信问题。

   (1)片段识别符(fragment identifier)

   (2)window.name

   (3)跨文档通信API(Cross-document messaging)


 4-2-1:片段识别符

     片段识别符指的是,URL的#号后面的部分,比如http://qq.com/x.html#fragment的#fragment。如果只是改变片段标识符,页面将不会重新刷新、

  父窗口可以把信息,写入子窗口的片段标识符。

 var src = originURL+'#'+data;
 document.getElementById('iframe').src = src;

  子窗口通过监听hashchange事件得到通知

  window.onhashchange = checkMessage;
  function checkMessage(){
  var message = window.location.hash;
  //...
 }

 同样的,子窗口也可以改变父窗口的片段标识符。

  parent.location.href = target+"#"+hash;

4-2-2:window.name:

浏览器窗口有window.name属性。这个属性的最大特点是,无论是否同源,只要在同一个窗口里,前一个网页设置这个属性,后一个网页就可以读取它

父窗口先发开一个子窗口,载入一个不同源的网页,该网页将信息写入window.name属性。

window.name = data;

接着,子窗口跳回一个与主窗口同域的网址。

location = 'http://parent.url.com/xxx.html';

  然后,主窗口就可以读取子窗口的window.name了。

var data = document.getElementById('iframe').contentWindow.name;

优点: window.name容量很大,可以防止非常长的字符串;

缺点必须监听子窗口window.name属性的变化,会影响网页性能。


4-2-3:跨文档消息传输window.postMessage:

上面两种方法都属于破解,HTML5为解决这个问题,引入一个全新的API:跨文档消息传输Cross Document Messaging。

下一代浏览器都将支持这个功能:Chrome 2.0+、Internet Explorer 8.0+, Firefox 3.0+, Opera 9.6+, 和 Safari 4.0+ 。 

Facebook已经使用了这个功能,用postMessage支持基于web的实时消息传递。


使用方法:otherWindow.postMessage(message, targetOrigin);

otherWindow: 对接收信息页面的window的引用。可以是页面中iframe的contentWindow属性;window.open的返回值;

通过name或下标从window.frames取到的值

message: 具体的信息内容,string类型

targetOrigin: 接受消息的窗口的源(origin),即“协议+域名+端口”。也可以设为“*”,表示不限制域名,向所有窗口发送

message事件的事件对象event,提供一下三个属性:

(1).event.source:发送消息的窗口

(2).event.origin:消息发向的网站

(3).event.data:消息内容

具体实例:

a.com/index.html中的代码:


b.com/index.html中的代码:

4-3、如何解决跨域LocalStorage。

通过window.postMessage,读写其他窗口的LocalStorage也成为了可能。下面是一个例子,主窗口写入iframe子窗口的LocalStorage。

window.onmessage = function(e){
  if(e.origin !== 'http://bbb.com'){
    return ;
 }
  var payload = JSON.parse(e.data);
  localStorage.setItem(payload.key,JSON.stringify(payload.data));
};

上面代码中,子窗口将父窗口发送来的消息,写入自己的LocalStorage。

父窗口发送消息的代码如下.

var win = document.getElementsByTagName('iframe')[0].contentWindow;
var obj = {name:'Jack'};
win.postMessage(JSON.stringify({key:'storage',data:obj}),'http://bbb.com');

加强版的子窗口接受消息的代码如下。

window.onmessage = function(e) {
  if (e.origin !== 'http://bbb.com') return;
  var payload = JSON.parse(e.data);
  switch (payload.method) {
    case 'set':
      localStorage.setItem(payload.key, JSON.stringify(payload.data));
      break;
    case 'get':
      var parent = window.parent;
      var data = localStorage.getItem(payload.key);
      parent.postMessage(data, 'http://aaa.com');
      break;
    case 'remove':
      localStorage.removeItem(payload.key);
      break;
  }
};

加强版的父窗口发送消息代码如下:

var win = document.getElementsByTagName('iframe')[0].contentWindow;
var obj = { name: 'Jack' };
// 存入对象
win.postMessage(JSON.stringify({key: 'storage', method: 'set', data: obj}), 'http://bbb.com');
// 读取对象
win.postMessage(JSON.stringify({key: 'storage', method: "get"}), "*");
window.onmessage = function(e) {
  if (e.origin != 'http://aaa.com') return;
  // "Jack"
  console.log(JSON.parse(e.data).name);
};

4-4、如何解决AJAX的跨域:

同源政策规定,AJAX请求只能发给同源的网址,否则就报错。
除了架设服务器代理(浏览器请求同源服务器,再由后者请求外部服务),有三种方法规避这个限制。

(1)JSONP

(2)WebSocket

(3)CORS


4-4-1、JSONP解决AJAX跨域问题:

JSONP是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,老式浏览器全部支持,服务器改造非常小。
它的基本思想是,网页通过添加一个

你可能感兴趣的:(Javascript)