同源策略

早期为了防止CSRF(跨域请求伪造)的攻击,浏览器引入了同源策略(SOP)来提高安全性。同源策略指的是以下3种必须相同

  • 协议
  • 域名
  • 端口

只有满足上面3中条件才能产生数据交互,这在一定程序上保证了网站的安全,请看下表

URL结果原因
http://store.company.com/dir2/other.html成功dir2/other.html
http://store.company.com/dir/inner/another.html成功dir/inner/another.html
https://store.company.com/secure.html失败不同的协议 ( https )
http://store.company.com:81/dir/etc.html失败不同的端口 ( 81 )
http://news.company.com/dir/other.html失败不同的主机 ( news )

JSONP原理

jsonp利用script标签没有跨域的限制,让服务器返回相应的函数可执行字符串,来达到获取不同源服务器的数据

所以可以这样实现

// 声明一个回调函数
function jsonp2(data) {
	alert(data);
}

var script = document.createElement("script");
script.type = "text/javascript";
script.src = "http://www.a.com?callback=jsonp2";
document.getElementsByTagName("head")[0].appendChild(script);

然后服务器根据callback参数返回对应的函数执行字符串

// express返回数据
res.send("jsonp2({a: 1, b: 2})");

这样一个简单的跨域请求就完成了

缺点:根据jsonp的原理,可以发现jsonp只能发送get请求,而不能post,put,delete等其他restful请求

jQuery实现

jQuery实现jsonp的例子

$.ajax({  
    type: "get",  
    async: false,  
    url: "http://www.a.com",  
    dataType: "jsonp",  
    jsonp: "callback", // 相当于上面的callback,可以自定义 
    jsonpCallback: "receive", // 回调函数名,相当于上面的jsonp2,默认jQuery自动生成随机函数名
    success: function (data) {  
        alert(data);  
    } 
}); 

document.domain

利用domain可以获取当前文档的服务器域名,这个属性也可以设置,但是值必须是当前域或其当前域的超级域,例如

www.a.com 可以设置 www.a.com 或者 a.com ,不能设置 b.com, test.a.com

举个栗子

http://www.a.com/index.html 的主页面有一个<iframe src="http://news.a.com/news.html"></iframe>

此时想要在index.html调用news.html的getNews()方法,只需在两个页面都设置 document.domain = "a.com" 就可以了

如果没设置会报错

Uncaught SecurityError: Blocked a frame with origin "http://localhost:3333" from accessing a frame with origin "http://localhost:3000". Protocols, domains, and ports must match.

设置好 domain 就可以调用iframe的方法了

window.frames["aframe"].window.getNews();
// 或者
document.getElementsByName("aframe")[0].contentWindow.getNews();
// 或者
document.getElementById("aframe").contentWindow.getNews();

window.name

这个比较少用,自行百度

window.postMessage

利用html5的新特性可以解决下面几个方法

1.页面和其打开的新窗口的数据传递

2.多窗口之间消息传递

3.页面与嵌套的iframe消息传递

在iframe页面监听 message 事件

window.addEventListener("message", receiveMessage, false);
function receiveMessage(event) {
    var origin = event.origin || event.originalEvent.origin; // For Chrome, the origin property is in the event.originalEvent object.
    alert(origin);
}

然后在当前页面获取对应的iframe的调用window.postMessage()方法,就可以和iframe交互,同理iframe和父窗体交互,当前页也需要监听message事件

window.frames['aframe'].window.postMessage("参数", "http://localhost:3000");

window.postMessage语法

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

message

将要发送到其他 window的数据。它将会被结构化克隆算法序列化。这意味着你可以不受什么限制的将数据对象安全的传送给目标窗口而无需自己序列化。

targetOrigin

通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。这个机制用来控制消息可以发送到哪些窗口;例如,当用postMessage传送密码时,这个参数就显得尤为重要,必须保证它的值与这条包含密码的信息的预期接受者的orign属性完全一致,来防止密码被恶意的第三方截获。如果你明确的知道消息应该发送到哪个窗口,那么请始终提供一个有确切值的targetOrigin,而不是*。不提供确切的目标将导致数据泄露到任何对数据感兴趣的恶意站点。

[transfer]可选

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

参考资料:

https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage