赵工的个人空间


专业技术部分转网页计算转业余爱好部分


  网站建设

首页 > 专业技术 > 网站建设 > HTML5跨源通信
HTML5跨源通信

浏览器遵循同源策略,不同站点的网页如果需要信息交换就比较复杂。基于需求,HTML5提供了两个通信模块,跨文档消息传输和跨源异步请求,利用这两个模块,可以在不同的Web应用域之间进行安全的通信。其中,跨文档消息传输用于不同域页面之间的脚本通信,跨源异步请求用于请求不同域的服务器资源。

一、跨文档消息传输:

跨文档消息传输用于实现在不同框架之间、窗口之间和标签之间的跨源通信,一切通信仅限于浏览器客户端。

1.跨文档消息传输的实现:

HTML5把postMessage接口定义为发送消息的标准方式,可使用window对象的postMessage方法向其他窗口发送消息。方法为:
refWindow.postMessage(message,targetOrigin);
其中,refWindow表示窗口对象的引用,要把消息传递到窗口中;message表示发送的消息,可以是一般的文本,也可以是转化成文本的对象;targetOrigin表示接收消息窗口所属的源的URL。
为了使接收消息的窗口能够接收到消息,还需要给窗口增加message事件,以监听其他窗口发送过来的消息。方法为:
window.addEventListener ("message", messageListener, false);
function messageListener(e) {
   console.log(e.origin);
   console.log(e.data);
   console.log(e.source);
}
上述代码中,message为事件名,触发该事件后,会调用一个回调函数messageListener,以处理接收过来的消息。消息事件包含一个消息来源origin、发送的数据data和源窗体对象实例resource。可以通过源信息,判断消息来源是否合法。

2.同源策略与跨文档通信安全:

同源策略,又名同域策略,是浏览器中主要的安全措施。这里的源指的是主机名、协议和端口号的组合,可以把一个源看作是某个Web页面或浏览器所浏览信息的创建者。同源策略,简单说就是要求动态内容(如JavaScript)只能阅读与之同源的那些HTTP应答和cookies,而不能阅读来自不同源的内容。
一般情况下,要通过跨源通信的消息事件中的源来确定消息来源是否可靠。同时也有必要增加事件监听器来监听不可信的干扰消息。一般都会提供一份白名单,以协助浏览器做安全处理,即对比消息来源是否在可信赖的白名单中。
另外,对于传递过来的消息数据,也应谨慎使用,即便是可靠的消息来源,也应该像对待外部输入时一样慎重。首先要避免传递HTML标记数据,因为不恰当的标记数据可能影响页面布局;其次应避免使用eval()方法来处理传递过来的数据,因为会发送不可预期的脚本执行,建议使用JSON对象的操作。示例:
element.textContent=e.data;
JSON.parse(e.data)

3.检测浏览器支持情况:

使用postMessage之前,需要检测浏览器是否支持。方法为:
if (typeof window.postMessage==="undefined") {
   alert("使用的浏览器不支持postMessage!");
}

4.发送消息:

通过调用目标页面的window对象中的postMessage()函数,可向目标页面发送消息。方法:
targetWindow.postMessage("Hello, world","www.example.com");
其中,postMessage的第一个参数表示发送的数据,第二个参数表示消息传送的目标页面的域,可以使用*表示对于消息传送的目标页面没有域的限制。
如果要发送消息给当前页面中的iframe,可以在相应iframe的contentWindow中调用postMessage()方法:
document.getElementsByTagName("iframe")[0].contentWindow.postMessage("Hi","*");

5.监听消息事件:

监听消息事件是在发送消息的目标页面进行的。通过在window对象中添加message事件,即可监听发送过来的消息。示例:
var originWhiteList = [ "http://portal.example.com", "http://games.example.com", "http://www.example.com"];
function checkWhiteList(origin) {
   for (var i=0;i<originWhiteList.length;i++) {
      if (origin===originWhiteList[i]) {
         return true;
      }
   }
   return false;
}
function messageHandler(e) {
   if (checkWhiteList(e.origin)) {
         //processMessage(e.data);
   } else {
         //忽略未确认的消息来源
   }
}
window.addEventListener("message",messageHandler,true);
上述代码中,首先设置了白名单,在接收信息时,只接收列入白名单的域发来的信息。checkWhiteList()是针对消息来源进行检测是否列入白名单。最重要的是添加了消息监听器message,并把消息交给messageHandler()函数来处理。函数messageHandler()的参数e为消息事件MessageEvent,通过该消息事件,可以访问消息来源origin、发送的数据data和源窗体对象实例resource等信息。
在部署文档信息传输时,监听消息的页面和发送消息的页面应从属于不同的域名,然后再建立两个页面的关联,如通过iframe或弹出页面等方式,以方便这两个页面获取到对方的窗口代理,最终实现信息交换。

6.消息事件接口MessageEvent:

Interface MessageEvent:Event {
   readonly attribute any data;
   readonly attribute DOMString origin;
   readonly attribute DOMString lastEventId;
   readonly attribute WindowProxy source;
   readonly attribute MessagePortArray ports;
   void initMessageEvent (in DOMString typeArg, in boolean canBunnleArg, in boolean cancelableArg, in any dataArg, in DOMString originArg, in DOMString lastEventIdArg, in WindowProxy sourceArg, in MessagePortArray portsArg);
};
HTML5定义的MessageEvent接口,也是WebSockets和Workers的一部分。在跨文档消息传输的应用中,一般会用到MessageEvent接口的data属性、origin属性、source属性和ports属性。
⑴MessageEvent属性:皆为只读
·data:数据属性,获取消息中的数据
·origin:来源属性,获取消息的来源,通常是一种格式,包含主机名和端口号
·lastEventId:最后事件的编号,应用于服务器发送事件,表示最后一个事件的源ID
·source:源代理属性,获取源窗口的代理
·ports:端口属性,获取消息的端口数组
⑵MessageEvent方法:只有一个
·initMessageEvent():初始化MessageEvent事件接口方法

二、跨源请求XMLHttpRequestLevel2:

XMLHttpRequest对象实现了Ajax的Web应用程序的关键功能,提供了对HTTP协议的完全访问,包括发起POST和HEAD请求以及普通的GET请求的能力,可以同步或异步返回Web服务器的响应,并且能以文本或者一个DOM文档形式返回内容。
XMLHttpRequestLevel2是XMLHttpRequest的增强版,添加了一些新功能,如跨站点的请求、进度事件、处理发送和接收字节流等。

1.改进的XMLHttpRequest对象:

使用跨源的XMLHttpRequest请求,使得浏览器中的页面可以访问不同的服务器资源,为构建非同源服务的Web应用程序提供了解决方案,如天气预报、股票信息、实时汇率等,去专门提供这些服务的服务器请求资源。随着互联网服务越来越精细化,把各种服务分别部署在不同的服务器上,不但提供专业化服务,而且便于管理、维护和扩展,用户通过浏览器的XMLHttpRequest跨源请求直接访问不同服务器的资源。
XMLHttpRequestLevel2为XMLHttpRequest对象新增了对进度的一系列响应事件,不但可以获取详细的进度信息,还可以跟踪资源上载的进度信息,其他新增功能还包括处理发送和接收字节流等。

2.XMLHttpRequestLevel2接口:

包括了2个主要的接口:
interface XMLHttpRequestEventTarget: EventTarget {
   attribute EventListener onabort;
   attribute EventListener onerror;
   attribute EventListener onload;
   attribute EventListener onloadstart;
   attribute EventListener onprogress;
};
[Constructor] interface XMLHttpRequest: XMLHttpRequestEventTarget {
   attribute EventListener onreadystatechange;
   const unsigned short UNSENT=0;
   const unsigned short OPENED=1;
   const unsigned short HEADERS_RECEIVED=2;
   const unsigned short LOADING=3;
   const unsigned short DONE=4;
   readonly attribute unsigned short readyState;
   attribute boolean withCredentials;
   void open (in DOMString method, in DOMString url);
   void open (in DOMString method, in DOMString url, in boolean async);
   void open (in DOMString method, in DOMString url, in boolean async, [Null = Null, Undefined = Null] in DOMString user);
   void open (in DOMString method, in DOMString url, in boolean async, [Null = Null, Undefined = Null] in DOMString user, [Null = Null, Undefined = Null] in DOMString password);
   void setRequestHeader(in DOMString header, in DOMString value);
   readonly attribute XMLHttpRequestUpload upload;
   void send ();
   void send (in ByteArray data);
   void send ([Null = Null, Undefined = Null] in DOMString data);
   void send (in Document data);
   void abort ();
   DOMString getAllResponseHeaders();
   DOMString getResponseHeader(in DOMString header);
   readonly attribute unsigned short status;
   readonly attribute DOMString statusText;
   void overrideMimeType (mime);
   readonly attribute ByteArray responseBody;
   readonly attribute DOMString responseText;
   readonly attribute Document responseXML;
};
在上述的两个接口清单中,XMLHttpRequestEventTarget中仅包含一系列的响应事件,而常用的XMLHttpRequest是继承XMLHttpRequestEventTarget接口的。
XMLHttpRequestLevel2还提供了未完善的XMLHttpRequestUpload接口:
interface XMLHttpRequestUpload: XMLHttpRequestEventTarget {
   //供未来使用
};

3.新的响应事件:

XMLHttpRequestLevel2完善了事件接口XMLHttpRequestEventTarget,包含以下监听事件:
·abort:当请求中断时的事件处理句柄。如调用了abort()方法。
·error:当请求失败时的事件处理句柄。
·load:当成功完成请求时的事件处理句柄。
·loadstart:当请求开始时的事件处理句柄。
·progress:当正在加载数据或发送数据时的事件处理句柄。

4.检测浏览器支持情况:

如果需要使用XMLHttpRequestLevel2下新的XMLHttpRequest对象功能,需要检测当前的浏览器是否支持该功能。常用的方法是检测XMLHttpRequest对象是否存在withCredentials属性。示例:
var xmlHttp=new XMLHttpRequest();
if (typeof xmlHttp.withCredentials==="undefined") {
   alert("使用的浏览器不支持跨源请求");
} else {
   alert("使用的浏览器支持跨源请求");
}

5.构建跨源请求:

构建跨源请求与构建同源请求在实现上基本上类似,只需要指定一个不是同源的请求地址即可。代码为:
xml.Http.open("GET","http://zhao:8081/cors.html",true);
xmlHttp.send(null);

6.添加监听事件:

在跨源请求发出之前,通过添加XMLHttpRequest对象的监听事件,可以跟踪请求的执行过程。示例:
xmlHttp.onabort=function(e) {
   console.log("请求中断");
}
xmlHttp.onerror=function(e) {
   console.log("请求失败");
}
xmlHttp.onload=function(e) {
   console.log("请求完成,输出请求内容");
   console.log(xmlHttp.responseText);
}
xmlHttp.onloadstart=function(e) {
   console.log("请求开始");
}
xmlHttp.onprogress=function(e) {
   console.log("中断中……");
   if (e.lengthComputable) {
      console.log(e.loaded+"/"+e.total);
   }
}
与之前常用的onreadystatechange事件相比,新增的事件可以更详细地跟踪请求的整个过程。其中通过使用onprogress事件,可以完成实时的进度信息。
所有事件的回调函数,都可以获取到XMLHttpRequestProgressEvent对象,这是一个进度对象,其中的信息记录了等待发送数据的总量、已发送数据的总量,以及数据总量是否已知等。
XMLHttpRequest.upload对象也包括上述事件,以及XMLHttpRequestProgressEvent对象。
xmlHttp.upload.onprogress=function(e) {
   console.log("上载中……");
   if (e.lengthComputable) {
      console.log(e.loaded+"/"+e.total);
   }
}

7.服务器部署:

要完成跨源请求,需要依赖两个条件:首先,使用XMLHttpRequest对象访问的页面与当前页面是不能同域的;其次,请求的目标服务器能够解析跨源的XMLHttpRequest对象请求。所以,需要对服务器进行一些相关的配置部署。
⑴IIS6中暴露Access-Control-Allow-Origin:
单击需要启用CORS的站点的属性,在“HTTP头”标签下的自定义Http头部分,添加如下信息:
自定义HTTP头名:Access-Control-Allow-Origin
自定义HTTP头值:*
⑵IIS7中可以复制以下代码到你的站点或应用程序根目录下的web.config文件中:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.webServer>
<httpProtocol>
  <customHeaders>
    <add name="Access-Control-Allow-Origin" value="*" />
  </customHeaders>
</httpProtocol>
  </system.webServer>
</configuration>

8.HTTP头:

HTTP是超文本传输协议的缩写,用于传送WWW方式的数据。HTTP协议采用了请求/响应模型。客户端向服务器发送一个请求,请求头包含请求的方法、URI、协议版本,以及包含请求修饰符、客户信息和内容的类似于MIME的消息结构。
HTTP头部信息通常包括客户机向服务器的请求消息和服务器向客户机的响应消息。以下是执行一个跨源请求的HTTP头部示例。
跨源请求的头部示例:
GET /cors.aspx HTTP/1.1
Host: zhao:8081
User-Agent: Mozilla/5.0 (Windows NT 5.2) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.121 Safari/535.2
Accept: */*
Connection: keep-alive
Cache-Control: max-age=0
Origin: http://dwen:8081
Referer: http://dwen:8081/code14-Test.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8
Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3
跨源响应的头部示例:
HTTP/1.1 200 OK
Date: Fri, 23 Dec 2011 01:41:25 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
Access-Control-Allow-Origin: http://dwen:8081
Access-Control-Allow-Credentials: true
X-AspNet-Version: 2.0.50727
Cache-Control: private
Content-Type: text/html
Charset=utf-8 Content-Length:12
跨源的头部信息会包含Origin和Referer信息,浏览器会帮助完成;而响应的头部信息中的Access-Control-Allow-Origin信息,则是在服务器部署的。

示例:通过按钮事件触发一个跨源请求,结果将显示在响应信息区中,包括进度状态

页面HTML:
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>跨源XMLHttpRequest请求</title>
  <link rel="stylesheet" href="ajaxrequest.css">
  <script src="ajaxrequest.js"></script>
</head>
<body>
<form name="form1" method="post" action="">
   <input type="button" name="button" value="请求资源" onclick="ajaxRequest()" />
   <div class="pad10">响应信息:
      <p id="response"></p>
   </div>
   <div id="progress" class="pad10">进度状态:
      <p>预备</p>
   </div>
</form>
</body>
</html>
脚本文件ajaxrequest.js:
function ajaxRequest() {
   var xmlHttp=new XMLHttpRequest();
   if (typeof xmlHttp.withCredentials==="undefined") {
     alert("使用的浏览器不支持跨源请求");
     return;
   }
   xmlHttp.onabort=function(e) {
      setProgress("请求中断");
   }
   xmlHttp.onerror=function(e) {
      setProgress("请求失败");
   }
   xmlHttp.onload=function(e) {
      setProgress("请求完成");
      var obj=document.getElementById("response");
      obj.innerText=xmlHttp.responseText;
   }
   xmlHttp.onloadstart=function(e) {
      setProgress("请求开始");
   }
   xmlHttp.onprogress=function(e) {
      setProgress("中断中……");
   }
   xmlHttp.open("get","http://yang:8081/cors.aspx",true);
   xmlHttp.send(null);
}
function setProgress(str) {
   var obj=document.getElementById("progress");
   var p=document.createElement("p");
   p.innerText=str;
   obj.appendChild(p);
}

Copyright@dwenzhao.cn All Rights Reserved   备案号:粤ICP备15026949号
联系邮箱:dwenzhao@163.com  QQ:1608288659