amenzai / myDiary

Record what you do every day
4 stars 0 forks source link

跨域通信的几个点 | 2018-3-12 #16

Closed amenzai closed 6 years ago

amenzai commented 6 years ago

受同源限制有如下几点:

// A网页是http://w1.example.com/a.html,B网页是http://w2.example.com/b.html
document.domain = 'example.com';

// 现在,A 网页通过脚本设置一个 Cookie。
document.cookie = "test1=hello";

// B 网页就可以读到这个 Cookie。
var allCookie = document.cookie;

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

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

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

Iframe

iframe窗口之中的脚本,可以获得父窗口和子窗口。但是,只有在同源的情况下,父窗口和子窗口才能通信;如果跨域,就无法拿到对方的DOM。

// 父窗口运行下面的命令,如果iframe窗口不是同源,就会报错。
document.getElementById("myIFrame").contentWindow.document
// Uncaught DOMException: Blocked a frame from accessing a cross-origin frame.

// 窗口获取主窗口的DOM也会报错。
window.parent.document.body
// 报错

这种情况还适用于window.open方法打开的窗口,只要跨域,父窗口与子窗口之间就无法通信。

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

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

片段识别符

URL的#号后面的部分。如果只是改变片段标识符,页面不会重新刷新。

// 父窗口可以把信息,写入子窗口的片段标识符。
var src = originURL + '#' + data;
document.getElementById('myIFrame').src = src;

// 子窗口通过监听hashchange事件得到通知。
window.onhashchange = checkMessage;

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

// 同样的,子窗口也可以改变父窗口的片段标识符。
parent.location.href= target + '#' + hash;

跨文档通信API(window.postMessage)

postMessage方法的第一个参数是具体的信息内容,第二个参数是接收消息的窗口的源(origin),即“协议 + 域名 + 端口”。也可以设为*,表示不限制域名,向所有窗口发送。

// 父窗口aaa.com向子窗口bbb.com发消息
var popup = window.open('http://bbb.com', 'title');
popup.postMessage('Hello World!', 'http://bbb.com');

// 子窗口向父窗口发送消息的写法类似。
window.opener.postMessage('Nice to see you', 'http://aaa.com');

// 父窗口和子窗口都可以通过message事件,监听对方的消息。
window.addEventListener('message', function(e) {
  console.log(e.data);
},false);

event对象的几个属性:

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

// 父窗口发送消息的代码如下。
var win = document.getElementsByTagName('iframe')[0].contentWindow;
var obj = { name: 'Jack' };
win.postMessage(JSON.stringify({key: 'storage', data: obj}), 'http://bbb.com');

Ajax

跨域解决:

JONP

它的基本思想是,网页通过添加一个 Githubissues.

  • Githubissues is a development platform for aggregating issues.