HTML5 实现跨域消息传递

来源:互联网 发布:哪里有im域名 编辑:程序博客网 时间:2024/06/05 17:37

同源策略

要理解跨域,首先要知道同源策略。所谓同源策略:是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少同源策略,则浏览器的正常功能可能受到影响。可以说,WEB是构建在同源策略上的。

同源:如果两个URL的域名、协议、端口相同,则表示它们同源。

浏览器的同源策略,限制了来自不同源的"文档"或脚本当前文档读取或设置属性。也就是其它源的文档或脚本不能对当前文档进行设置属性和读取操作。根据这个策略,a.com域名下的JavaScript无法跨域操作b.com域名下的对象。比如:baidu.com域名下的页面中包含的JavaScript代码不能访问Google.com域名下的页面内容。

同源策略在现实生活中是十分重要的。比如:假设攻击者利用iframe把真正的银行登录页面嵌到自己的页面上,当用户使用真正的用户名、密码登录时,该页面的JavaScript会读取用户表单中的内容,这是不安全的。

在浏览器中,<script>、<link>、<img>、<iframe>等标签都可以加载跨域资源,不受同源策略限制。但是通过"src"加载的资源,浏览器限制了JavaScript的权限、不能进行任何的读写操作。从而,即使请求成功了,数据回来了也是取不到的。

一些浏览器窗口和标签之间都是完全相互独立的,在其中一个窗口或者标签运行的代码其它窗口或标签中是完全无法识别的。但在一些情况下,当脚本打开一个新窗口或者在嵌套的窗体中运行的时候,多个窗口之间、窗体之间是可识别的。如果它们的文档是在同一个WEB服务器下,那么各个窗口中的脚本是可以相互交互和相互操作对方的页面的。

但在有时候,尽管脚本可以引用其它的window对象,但由于那个窗口的内容不同源,那么Web浏览器是不能访问到这个窗口中的内容的。大部分情况下,浏览器是不允许脚本访问其它窗口中的内容、不允许读取其它窗口中的属性或操作其它窗口的方法。

HTML5中提供了postMessage()方法用于 通过异步消息传递的方式 使不同源之间的脚本可以相互访问、可以相互读取不同源窗口中的内容


postMessage()实现跨域

语法:window.postMessage(msg, targetOrigin);

其中,window指目标窗口,可能是window.frames属性的成员或者是由window.open创建的窗口。

  • msg:要发送的消息,html5规范中提到该参数可以是JavaScript的任意基本类型或复制的对象,然而并不是所有浏览器都可以做到,部分浏览器只能处理字符串参数,因此需要使用JSON.stringify()方法对对象参数序列化。
  • targetOrigin:目标域、目标窗口地址,包括:协议、主机名、端口号。postMessage()会将消息传递给指定的这个窗口,而不会将其消息传递给其它窗口。如果这个消息不是敏感消息,可以指定该窗口为"*",则表示传递给任意窗口。若指定窗口为"/",则表示和当前窗口的同源窗口,如:在本地服务器localhost:8080/下的名为index.html和index2.html的两个页面,就是同源窗口。

获取postMessage()传来的消息:为页面(目标窗口)添加onmessage事件处理程序。
window.addEventListener("message", function (e) {})

如果指定的源匹配成功的话,那么当调用postMessage()方法的时候,在目标窗口的window对象上就会触发message事件。为目标窗口添加onmessage事件处理程序。onmessage事件接收一个参数e,它是一个event对象。该参数有几个重要属性:
  • data:postMessage传递过来的msg。获取postMessage()传递过来的消息。
  • 发送消息的窗口对象。
  • origin:发送消息窗口的源(协议+主机+端口号),也就是消息来源即域名。


下面写一个简单的demo来说明跨域实现消息传递:本人是在本地服务器测试(localhost:8080;//)

1、首先是发送数据的页面index.html,其地址为:http://localhost:8080/index.html
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>这是发送数据的页面index</title><style>    </style></head><body>    <iframe id="iframe" src="http://localhost:8080/index2.html"></iframe>    <input id="msg" type="text" placeholder="请输入要发送的消息">    <button id="send">发送</button></body><script>    window.onload = function () {    var but = document.getElementById("send");    but.onclick = function () {        var msg = document.getElementById("msg").value; //要发送的消息数据    var iframeWindow = document.getElementById("iframe").contentWindow; //iframe所在的window对象。            //将msg消息数据发送到不同源下的index2页面中    iframeWindow.postMessage(msg, "http://localhost:8080/index2.html");    };    };</script></html>

其中,contentWindow表示iframe窗口中的window对象。是iframe窗口体的window对象调用postMessage()方法来发送数据。


2、其次是接收数据的页面Index2.html,其地址为:http://localhost:8080/index2.html
<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <title>接收数据的页面index2</title></head><body>    <div>        <h3>inex2.html页面,以下是接收到的消息:</h3>        <p id="msg">        </p>    </div></body><script>    window.onload = function () {        //添加onmessage事件处理程序,用于接收postMessage()发送的数据。        //检测该浏览器是IE浏览器还是其它浏览器        if (window.addEventListener) {            window.addEventListener("message", handleMessage, false);        } else {            window.attachEvent("onmessage", handleMessage);        }        function handleMessage (event) {            event = event || window.event;            if (event.origin === "http://localhost:8080") { //判断发送数据的窗口的源                document.getElementById("msg").innerHTML = event.data; //将从index.html中接收到的消息输入index2.html页面中。            }        }    };</script></html>

实例效果:



当在index页面中的输入框中输入内容并点击按钮就会发送数据,index2中就会收到index中发送过来的数据。在本实例中,index.html和index2.html是同在localhost:8080/下的两个页面,所以postMessage()方法的第二个参数可以写成"/",即:
iframeWindow.postMessage(msg, "/");



实例回顾:

1、在主页面index中发送数据,利用postMessage()方法来发送数据,第一个参数为需要发送的数据,第二个参数为目标域,即要将数据发送到的窗口或页面。注意:这里是用目标窗口的window对象调用postMessage()方法的。

2、在目标页面index2中,为postMessage()发送过来的数据添加onmessage事件处理程序,并用event.origin判断是否存在目标域,并接收来自主页面发送过来的数据(event.data)。

也就是说,postMessage()方法是编写在主页面、窗口中的,即发送数据的窗口。postMessage()发送数据后,会触发message事件,在目标窗口中添加onmessage事件处理程序用于接收来自主页面的数据消息。