跨域访问实践
同源策略
An origin is defined by the scheme, host, and port of a URL. Generally speaking, documents retrieved from distinct origins are isolated from each other.
https://developer.mozilla.org/zh-CN/docs/Same-origin_policy_for_file:_URIs
http://www.w3.org/Security/wiki/Same_Origin_Policy
对于一些同源策略, 是HTML5新定义, 是否可使用,可以查询如下网站:
http://caniuse.com/
iframe方式嵌入页面的操作实验
javascript的同源策略,见如下文章:
https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy?redirectlocale=zh-CN&redirectslug=JavaScript%E7%9A%84%E5%90%8C%E6%BA%90%E7%AD%96%E7%95%A5
只有在 协议 域名 和 端口 完全一致的情况下, 才认为, 两个页面对应同一个源。
下面使用iframe和js api,构造跨域访问实例。
js api 为 iframe.contentDocument, 父亲页面通过此js api访问iframe内嵌页面的内容, 详细见下文:
https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement
父亲页面为: a.html, 内嵌一个 iframe, 引用b.html(域名为 localhost)
父亲页面加载后, 访问内嵌页面的 ID为binput 输入框的值。
<html> <body> <h1> hi, this is a.html, frame below show b.html </h1> <iframe src="http://localhost/b.html"></iframe> <script type="text/javascript"> window.onload = function(){ console.log("a.html loaded"); var frame = document.getElementsByTagName("iframe"); console.log("frame src="+frame[0].src); var binput = frame[0].contentDocument.getElementById("binput"); console.log("b.html binput value="+binput.value); } </script> </body> </html>
<html> <body> <h1> hi, this is b.html. this page call by localhost domain. </h1> <input id="binput" value="binput"/> </body> </html>
实验结果:
使用http://localhost/a.html访问, 查看控制台, 可以发现 a可以访问b的binput。
使用http://127.0.0.1/a.html访问, 查看控制台, 可以发现浏览器有报错, (b.html使用localhost域名访问),chrome浏览器访问报错如下:
a.html loaded a.html:9
frame src=http://localhost/b.html a.html:11
Uncaught SecurityError: Failed to read the 'contentDocument' property from 'HTMLIFrameElement': Blocked a frame with origin "http://127.0.0.1" from accessing a frame with origin "http://localhost". Protocols, domains, and ports must match. a.html:12
CORS实践
ajax不能跨域访问, 同iframe跨域访问相同, 都是受同源策略影响。
CORS 为ajax跨域访问的一种解决方案,详细见下文:
http://www.cnblogs.com/Darren_code/p/cors.html
CORS W3C规范:
http://www.w3.org/TR/cors/
网上介绍的例子, 可见 ajax 请求跨域,是现到达服务器,然后根据响应来决定是否在客户端显示响应的。
http://blog.csdn.net/hfahe/article/details/7730944
服务器: index.php
添加跨域可访问的 header 头,可以任何网站访问:
<?php header("Access-Control-Allow-Origin:*"); echo "hello ".$_SERVER['HTTP_HOST']; exit; ?> Something is wrong with the XAMPP installation :-(
客户端: index.html
其中的ajax使用127.0.0.1域名访问index.php
<html> <head> <style> </style> </head> <body> <h1>hello world!</h1> <input type="text" value="test"/> <input type="button" value="button"/> <script type='text/javascript'> function createCORSRequest(method, url) { var xhr = new XMLHttpRequest(); if ("withCredentials" in xhr) { // 此时即支持CORS的情况 // 检查XMLHttpRequest对象是否有“withCredentials”属性 // “withCredentials”仅存在于XMLHTTPRequest2对象里 xhr.open(method, url, true); } else if (typeof XDomainRequest != "undefined") { // 否则检查是否支持XDomainRequest,IE8和IE9支持 // XDomainRequest仅存在于IE中,是IE用于支持CORS请求的方式 xhr = new XDomainRequest(); xhr.open(method, url); } else { // 否则,浏览器不支持CORS xhr = null; } return xhr; } var xhr = createCORSRequest('GET', "http://127.0.0.1/index.php"); if (!xhr) { throw new Error('CORS not supported'); } xhr.onreadystatechange = function processRequest() { if (xhr.readyState == 4) // 判断对象状态 { if (xhr.status == 200) // 请求结果已经成功返回 { alert(xhr.responseText); } } }; xhr.send(); </script> </body> </html>
如果页面使用 http://127.0.0.1/index.html访问, ajax属于站内访问,可以执行成功,
如果页面使用 http://localhost/index.html访问, ajax属于跨站访问,执行报错:
XMLHttpRequest cannot load http://127.0.0.1/index.php. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.
nginx配置跨域
安全的跨源文档通信(Cross Document Messaging)
postMessage
https://developer.mozilla.org/zh-CN/docs/Web/API/window.postMessage
http://www.ibm.com/developerworks/cn/web/1301_jiangjj_html5message/index.html
示例来源用途第二个网址:
服务器localhost/a.php使用postMessage传送 消息到 内嵌的不同域页面 127.0.0.1/b.php:
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Test Cross-domain communication using HTML5</title> <script type="text/JavaScript"> function sendIt(){ // 通过 postMessage 向子窗口发送数据 document.getElementById("otherPage").contentWindow .postMessage( document.getElementById("message").value, "http://127.0.0.1/" ); } </script> </head> <body> <!-- 通过 iframe 嵌入子页面 --> <iframe src="http://127.0.0.1/b.php" id="otherPage"></iframe> <br/><br/> <input type="text" id="message"><input type="button" value="Send to child.com" onclick="sendIt()" /> </body> </html>
b.php
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Web page from child.com</title> <script type="text/JavaScript"> //event 参数中有 data 属性,就是父窗口发送过来的数据 window.addEventListener("message", function( event ) { // 把父窗口发送过来的数据显示在子窗口中 document.getElementById("content").innerHTML+=event.data+"<br/>"; }, false ); </script> </head> <body> Web page from http://127.0.0.1 <div id="content"></div> </body> </html>
阻止页面被iframe内嵌
使用报文头标识 : X-Frame-Options
https://developer.mozilla.org/en-US/docs/Web/HTTP/X-Frame-Options
详细解释见下面说明:
Using X-Frame-Options
There are three possible values for X-Frame-Options:
DENY
- The page cannot be displayed in a frame, regardless of the site attempting to do so.
SAMEORIGIN
- The page can only be displayed in a frame on the same origin as the page itself.
ALLOW-FROM uri
- The page can only be displayed in a frame on the specified origin.
a.php 内嵌 b.php , 使用locahost引用:
<html> <body> <h1> hi, this is a.php, frame below show b.php using domain localhost </h1> <iframe src="http://localhost/b.php"></iframe> </body> </html>
b.php, 通过头decalare内嵌权限:
<?php
//header("X-Frame-Options: DENY"); // 不管地址栏访问 localhost/a.php 还是 127.0.0.1/a.php,都不能显示b.php
//header("X-Frame-Options: SAMEORIGIN"); // 只有使用 localhost/a.php,才能能显示b.php
header("X-Frame-Options: ALLOW-FROM 127.0.0.1"); // 即使使用 127.0.0.1/a.php,也能显示b.php
?>
<html> <body> <h1> hi, this is b.html. this page call by localhost domain. </h1> <input id="binput" value="binput"/> </body> </html>