HTML5学习笔记:跨域
在跨域安全性方面,有多个地方会有限制,主要是XMLHttpRequest对象的跨域限制和iFrame的跨域限制,下面我们分别来看一下。
Ajax跨域(CORS)
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。
它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
以PHP服务端为例
如果一个请求允许http://www.example.com的域请求的话,可以在PHP中这么写:
header("Access-Control-Allow-Origin:http://www.example.com");
如果有多个允许跨域访问的地址,可以添加多条或使用“,”号进行分隔,如果希望所有来源的地址都可以访问当前的页面,可以如下:
header("Access-Control-Allow-Origin:*");
同时,也可以控制请求的方法,如下是允许GET和POST两种请求方法:
header("Access-Control-Allow-Method:POST,GET");
IE中,要跨域需要使用到XDomainRequest的对象,我们这里就不展开来说了,而在其它浏览器中,使用XMLHttpRequest对象即可,并没有不一样的地方。
一个需要注意的地方
最近在公司开发的过程中,遇见的情况是PHP已经添加了header("Access-Control-Allow-Origin:*");代码,但是客户端请求时,仍然报错,最终查出的bug是,包含的其它PHP中,也添加了相同的代码,导致服务端返回时,在Chrome的Network页面中,发现有两条一样的头部数据,去掉一条即可。
CORS跨域之前的跨域方法
在CORS标准出现之前,XHR对象是不能跨域请求的,不过开发人员凭借自己的聪明才智,绕过了XHR对象,创造了另外几种可以跨域请求的方式,我们下面开简单的看看。
图像Ping
前端里面图片元素没有跨域的限制,所以我们可以通过模拟请求一个图片的方法,向服务端发送数据,服务端对应的页面可以返回一个简单的图片数据或者什么数据都不返回(客户端得到204状态)。
1 var img = new Image();
2 img.onload = img.onerror = function () {
3 console.log("Done");
4 }
5 img.src = "http://www.example.com/test.php?name=LiLei&age=28";
缺点是可以得到请求成功和失败的回调但是得不到服务端返回的数据,除非将数据放入图片返回。
统计在线广告浏览量等不需要返回数据的情况下通常使用该方式。
JSONP
即JSON with padding的简写,简单来说:就是通过添加一个script标签,通过创建该标签的src地址来传递参数,而服务端返回JS代码的内容,JS代码回调一个页面中已经存在的JS方法,同时将需要给到客户端的信息作为参数传递即可。这样就可以绕过XHR对象实现跨域请求并得到服务端返回的数据。
1 function handleResponse (response) {
2 console.log("name: " + response.name + ", age: " + response.age);
3 }
4
5 var script = document.createElement("script");
6 script.src = "http://www.example.com/test.php?name=LiLei&age=28";
7 document.body.insertBefore(script, document.body.firstChild);
当前代码中的handleResponse方法即服务端会回调的方法。
handleResponse({"name":"Han Meimei", "age":27});
服务端返回上面的文本后,由于是添加的script脚本,所以会调用到handleResponse方法并得到服务端的数据。
JSONP缺点
- 访问的其他域如果不安全,可能会返回一些有害的JS代码到当前页面进行执行;
- 难以确定请求失败的响应,需要用户自己实现一个超时计时器。
iFrame跨域
JavaScript出于安全方面的考虑,不允许iFrame跨域访问和调用其他页面的对象。
试想一下,如果我们做了一个钓鱼网站,使用一个iFrame引入了XX银行的首页,把自己伪装成该银行首页,此时如果我们可以跨域调用和修改XX银行的所有数据,也可以通过修改和注入JS代码,后果就是:如果有人登录了我们的假网站,我们就可以通过给这个iFrame添加我们自己的JS代码来轻松获得这个人的帐号和密码信息。
所以,iFrame不允许跨域访问是基于安全的考虑,也是必要的一个安全限制。
我们看下,具体的跨域限制:
- http://www.a.com/a.js http://www.a.com/b.js 同一域名下 允许
- http://www.a.com/lab/a.js http://www.a.com/script/b.js 同一域名下不同文件夹 允许
- http://www.a.com:8000/a.js http://www.a.com/b.js 同一域名,不同端口 不允许
- http://www.a.com/a.js https://www.a.com/b.js 同一域名,不同协议 不允许
- http://www.a.com/a.js http://70.32.92.74/b.js 域名和域名对应ip 不允许
- http://www.a.com/a.js http://script.a.com/b.js 主域相同,子域不同 不允许
- http://www.a.com/a.js http://a.com/b.js 同一域名,不同二级域名(同上) 不允许(cookie这种情况下也不允许访问)
- http://www.cnblogs.com/a.js http://www.a.com/b.js 不同域名 不允许
但是不可否认的是,在某些情况下,我们还是希望不同域的页面之间可以相互通信(注意这里不一定要相互可以调用修改)。下面我们来看看如何实现跨域的消息通信。
document.domain
通过修改多个框架domain属性为同样的值,可以突破跨域的限制,使多个页面之间可以互相访问到。
但是domain属性的修改有下面两个限制。
只能修改子域为主域,不能修改为其它域
域example.com嵌入了域p2p.example.com的页面,通过修改p2p.example.com的domain即可实现双方互相访问和修改,如同没有跨域一样,但是要注意不能改为其它域:
1 document.domain = "example.com"; // 成功
2 document.domain = "baidu.com"; // 报错
不能将主域修改为子域
如下,位于p2p.example.com的页面:
1 document.domain = "example.com"; // 成功
2 document.domain = "p2p.example.com"; // 报错
因为已经设置为主域了,再次设置会子域会报错。
使用postMessage
跨文档消息传递(cross-document messaging),简称XDM,在H5中该功能可以用来向iFrame嵌套的页面和嵌套自己的页面相互发送消息,也可以向当前页面弹出的窗口相互传递消息。
通过XDM我们可以安全的实现页面之间的跨域消息传递。
XDM得核心方法是postMessage,postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。
postMessage(data,origin)方法接受两个参数:
- data:要传递的数据,html5规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify()方法对对象参数序列化,在低版本IE中引用json2.js可以实现类似效果。
- origin:字符串参数,指明目标窗口的源,协议+主机+端口号[+URL],URL会被忽略,所以可以不写,这个参数是为了安全考虑,postMessage()方法只会将message传递给指定窗口,当然如果愿意也可以建参数设置为"*",这样可以传递给任意窗口。
而在发送了消息之后,符合条件(iFrame嵌套页面或弹出窗口,且符合postMessage的origin参数的域)的其他页面的window对象会收到该消息,作为“message”事件抛出该消息,对应的event对象有如下主要属性:
- data:顾名思义,是传递来的message;
- source:发送消息的窗口对象;
- origin:发送消息窗口的源(协议+主机+端口号)。
下面我们看一个例子:
A.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5 <title>A</title>
6 <body>
7 <p>A</p>
8 <input type="button" value="发送消息到frameB并得到frameB的回应" onclick="sendMsg()">
9 <iframe id="frameB" src="./B.html" style="width: 90%;"></iframe>
10 <script type="text/javascript">
11 window.addEventListener("message", function (event) {
12 console.log("A.html接收到消息:" + event.data);
13 });
14
15 function sendMsg () {
16 var frameB = document.getElementById("frameB");
17 frameB.contentWindow.postMessage("Hello I am Li Lei", "*");
18 }
19 </script>
20 </body>
21 </html>
B.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5 <title>B</title>
6 </head>
7 <body bgcolor="#ff0000">
8 <p>B</p>
9 <script type="text/javascript">
10 window.addEventListener("message", function (event) {
11 console.log("B.html接收到消息:" + event.data);
12 //向发送消息的window回送消息
13 event.source.postMessage("Hi, I am Han Meimei!", "*");
14 });
15 </script>
16 </body>
17 </html>
需要注意,window对象发送的消息只有自身可以接收到message消息。
图片crossOrigin属性
我们在请求其它域下的图片时,可以显示出来,但是当调用toBlob(),
toDataURL()
和getImageData()
等方法时会抛出安全错误,这是由于浏览器的安全策略导致的。
第一步,服务端配置图片类型资源的跨域范围权限,如Apache服务器可以如下配置:
1 <IfModule mod_setenvif.c>
2 <IfModule mod_headers.c>
3 <FilesMatch "\.(cur|gif|ico|jpe?g|png|svgz?|webp)$">
4 SetEnvIf Origin ":" IS_CORS
5 Header set Access-Control-Allow-Origin "*" env=IS_CORS
6 </FilesMatch>
7 </IfModule>
8 </IfModule>
第二步,在浏览器端可以设定Image对象或img标签的crossOrigin属性为"Anonymous"即可。
HTML5学习笔记:跨域的更多相关文章
- jw player学习笔记----跨域请求
需求来源:播放器皮肤文件请求不到,被限制了. 参考官网解决方案: http://www.longtailvideo.com/support/jw-player/28844/crossdomain-fi ...
- AJAX学习笔记——跨域
跨域 一个域名地址的组成 http:// www abc.com : 8080 / scripts/jquery.js 协议 子域名 主域名 端口号 请求资源地址 端口号:一般来说域名端口号是80,如 ...
- html5学习笔记一
HTML5学习笔记 <video>标记:定义视频,Ogg.MPEG4.WebM三种格式 <video src=”movie.ogg” controls=”controls”> ...
- [转]html5: postMessage解决跨域和跨页面通信的问题
[转]html5: postMessage解决跨域和跨页面通信的问题 平时做web开发的时候关于消息传递,除了客户端与服务器传值,还有几个经常会遇到的问题: 多窗口之间消息传递(newWin = wi ...
- vue.js学习之 跨域请求代理与axios传参
vue.js学习之 跨域请求代理与axios传参 一:跨域请求代理 1:打开config/index.js module.exports{ dev: { } } 在这里面找到proxyTable{}, ...
- Html5学习笔记1 元素 标签 属性
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 【Java Web开发学习】跨域请求
[Java Web开发学习]跨域请求 ================================================= 1.使用jsonp ===================== ...
- HTML5学习笔记之表格标签
HTML5学习笔记之表格标签 其他HTML5相关文章 HTML5学习笔记之HTML5基本介绍 HTML5学习笔记之基础标签 HTML5学习笔记之表格标签 HTML5学习笔记之表单标签 HTML5学习笔 ...
- HTML5学习笔记(二十八):跨域
在跨域安全性方面,有多个地方会有限制,主要是XMLHttpRequest对象的跨域限制和iFrame的跨域限制,下面我们分别来看一下. Ajax跨域(CORS) CORS是一个W3C标准,全称是&qu ...
随机推荐
- 几个比较实用的CSS
1.filter:chroma(color:#FFFFFF); 让指定的背景色透明,例: <table cellspacing = "0" cellpadding = ...
- 深入迁出mybatis系列
本系列教程均源于南柯梦,经好库文摘转发,感谢南柯梦的辛苦整理. 深入迁出mybatis系列 深入浅出Mybatis系列(一)---Mybatis入门 深入浅出Mybatis系列(二)---配置简介(m ...
- interface关键字定义接口
package interface0; public interface InterfaceTest { /* * 接口的定义,使用interface关键字定义接口 */ public interfa ...
- JS 解析JSON实现导序
var chartData = [ { "online": '2013-10-23', "new": 0.00, "login": 0.00 ...
- 【LOJ】#2128. 「HAOI2015」数字串拆分
题解 题中给的函数可以用矩阵快速幂递推 我们记一个数组dp[i](这个数组每个元素是一个矩阵)表示从1到i所有的数字经过拆分矩阵递推的加和 转移方法是 \(dp[i] = \sum_{j = 0}^{ ...
- vs2010添加头文件与库文件搜索目录
1 添加头文件搜索目录 项目属性->配置属性->C/C++->常规->添加包含目录 2 添加库文件搜索 项目属性->配置属性->VC++目录->库目录
- 家庭房产L2-007
较为麻烦的并查集 主要是我的模板是错的检查了好久.... 先是输入 把每个家庭连在一起 输出的家庭编号为该家庭所有编号的最小值 在并查集里面完成 第一次 0~n-1遍历储存好 家庭编号 和房子面积和 ...
- chrome书签(收藏栏)的导入导出
- P3397 地毯
P3397 地毯 前缀和最开始接触是在日照夏令营,lca的一段子树中加或减一个数然后打标记,求前缀和000+10000-100000000111110000二维也一样,比如对子矩阵都加10+10000 ...
- 001.VNC介绍
一 VNC介绍 VNC 服务是一个自由开源软件,采用RFB通信协议.RFB ("remote 帧缓存 ") 是一个远程图形用户的简单协议,因为它工作在帧缓存级别上,所以它可以应用于 ...