前端跨域问题

同源策略

同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。

意义:同源策略是一个用于隔离潜在恶意文件的重要安全机制。

只要协议、域名、端口有任何一个不同,都被当作是不同的源。只有三者都相同才能被认定是同源。

跨域

一般来说,跨域包括两种:

  • 主域相同的跨域
  • 完全不同的跨域

主域相同的跨域

举个例子:
页面一: a.com/a.html
页面二: script.a.com/b.html
这两个页面的主域都是 a.com ,所以它们之间的通信属于 主域相同的跨域。
我们只要把 "a.com/a.html" 和 "script.a.com/b.html"这两个页面的document.domain都设成相同的域名就可以了。
注意:document.domain的设置是有限制的,我们只能把document.domain设置成自身或更高一级的父域,且主域必须相同。

完全不同源的跨域

如果两个完全不同源的页面之间需要通讯,常见的做法主要包括

  • window.name 跨域
  • location.hash 跨域
  • window.postMessage 跨域

window.name

window 对象有个属性叫 window.name,这个属性有一个特点:

在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,
每个页面对window.name都有读写的权限,
window.name是持久存在一个窗口载入过的所有页面中的

window.name的值在不同页面加载后依旧存在(如果没修改则值不会变化),并且可以支持非常长的 name 值(2MB)。
如果是与iframe通信的场景就需要把iframe的src设置成当前域的一个页面地址。

缺点:传输的数据只能是字符串类型

location.hash

原理:父窗口可以读写子窗口的URL,子窗口只能读写相同域父窗口的URL。为了实现跨域,不同域的子窗口就必须借助一个与父窗口同域的代理。
举个例子,页面a: a.com/a.html 下有一个 src 为 「b.com/b.html」的 iframe
目的:希望a.html 和 b.html 能双向通信。

1.如果 a.html 想给 b.html 发信息
只需要修改 <iframe>src b.com/b.html -> b.com/b.html#message。
b.html 访问自己的 location.hash 就能拿到数据。

2.如果 b.html想给 a.html 发信息;b由于不能修改不同域父窗口的URL,所以b页面需要动态创建一个和父窗口同域的iframe来做代理:
b页面创建一个src为a.com/proxy.html#data的子窗口
这个proxy页面通过onhashchange(兼容情况)事件监听自己href的变化,事件触发后通过修改a页面的hash来达到传递数据的功能
a页面访问自己的location.hash即可拿到数据

官方方法 PostMessage

无论是否同源都可以使用该方法
window.postMessage() 方法被调用时,会在所有页面脚本执行完毕之后。
语法

otherWindow.postMessage(message, targetOrigin, [transfer]);
  • message

是要发送的数据,它将会被结构化克隆算法序列化

  • targetOrigin

通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个URI。**如果你明确的知道消息应该发送到哪个窗口,那么请始终提供一个有确切值的targetOrigin,而不是 *。不提供确切的目标将导致数据泄露到任何对数据感兴趣的恶意站点。**

  • transfer 可选

是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。

注意:
任何窗口可以在任何其他窗口访问此方法,在任何时间,无论文档在窗口中的位置,向其发送消息。 因此,用于接收消息的任何事件监听器必须首先使用origin和source属性来检查消息的发送者的身份。 这不能低估:无法检查origin和source属性会导致跨站点脚本攻击。

// 一个页面(a页面:"http://example.com:8080")发送信息
var popup = window.open(...popup details...);
popup.postMessage("hello there!", "http://example.org");
// a页面用户捕获其他页面发来的信息的方法
window.addEventListener("message", receiveMessage, false);

function receiveMessage(event)
{
  // 我们能相信信息的发送者吗?  (也许这个发送者和我们最初打开的不是同一个页面).
  if (event.origin !== "http://example.org")
    return;

  // event.source 是我们通过window.open打开的弹出页面 popup
  // event.data 是 popup发送给当前页面的消息 "hi there yourself!  the secret response is: rheeeeet!"
}
// 另一个页面(b页面:"http://example.org")捕获信息,用receiveMessage 处理信息
window.addEventListener("message", receiveMessage, false);

function receiveMessage(event)
{
  // 我们能信任信息来源吗?
  if (event.origin !== "http://example.com:8080")
    return;

  // event.source 就当前弹出页的来源页面
  // event.data 是 "hello there!"

  // 假设你已经验证了所受到信息的origin (任何时候你都应该这样做), 一个很方便的方式就是把enent.source
  // 作为回信的对象,并且把event.origin作为targetOrigin
  event.source.postMessage("hi there yourself!  the secret response " +
                           "is: rheeeeet!",
                           event.origin);
}
Comments
Write a Comment