HTML5 - 同源限制以及跨域请求

杨旭 bio photo By 杨旭

当一个资源从与该资源本身所在的服务器不同的域或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。

比如,站点 http://domain-a.com 的某 HTML 页面通过 的 src 请求 http://domain-b.com/image.jpg。网络上的许多页面都会加载来自不同域的CSS样式表,图像和脚本等资源。

出于安全考虑,浏览器会限制从脚本内发起的跨域HTTP请求。例如,XMLHttpRequest 和 Fetch 遵循同源策略。因此,使用 XMLHttpRequest或 Fetch 的Web应用程序只能将HTTP请求发送到其自己的域。为了改进Web应用程序,开发人员要求浏览器厂商允许跨域请求。

同源限制

同源是指:协议相同、域名相同、端口相同

为了保障用户的信息不被恶意获取或篡改,浏览器会限制如下行为:

  • Cookie、LocalStorage 和 IndexDB 无法读取。
  • 不能获取dom对象
  • 不能发起数据请求

绕开同源限制的方式

通过片段标识符

父窗口通过iframe加载自页面,当父窗口希望发送消息给子窗口时,可以通过修改url的片段标示符(http://example.com/x.html#fragment)来发送消息。

修改片段标识符(fragment)不会导致子窗口刷新

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

同时,子窗口可以通过监听hashchange事件来获取消息

window.onhashchange = checkMessage;

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

postMessage

HTML5为了解决这个问题,引入了一个全新的API:跨文档通信 API(Cross-document messaging)

这个API为window对象新增了一个window.postMessage方法,允许跨窗口通信,不论这两个窗口是否同源。

例如,父窗口为http://parent.com通过iframe加载子窗口http://child.com

var popup = window.open('http://child.com', 'title');
popup.postMessage('Hello World!', 'http://child.com');
  • postMessage的第一个参数指定发送信息
  • postMessage的第二个参数指定发送目标
  • 子窗口同样可以向父窗口发送信息
window.opener.postMessage('Nice to see you', 'http://parent.com');

在接收消息的窗口中,通过事件来接收消息:

window.addEventListener('message', function(e) {
  console.log(e.data);
},false);

为了安全考虑,接收者必须校验消息来源:

window.addEventListener('message', receiveMessage);

function receiveMessage(event) {
  if (event.origin !== 'http://parent.com') return;
  console.log(event.data);
}