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状态)。
var img = new Image();
img.onload = img.onerror = function () {
console.log("Done");
}
img.src = "http://www.example.com/test.php?name=LiLei&age=28";
缺点是可以得到请求成功和失败的回调但是得不到服务端返回的数据,除非将数据放入图片返回。
统计在线广告浏览量等不需要返回数据的情况下通常使用该方式。
JSONP
即JSON with padding的简写,简单来说:就是通过添加一个script标签,通过创建该标签的src地址来传递参数,而服务端返回JS代码的内容,JS代码回调一个页面中已经存在的JS方法,同时将需要给到客户端的信息作为参数传递即可。这样就可以绕过XHR对象实现跨域请求并得到服务端返回的数据。
function handleResponse (response) {
console.log("name: " + response.name + ", age: " + response.age);
} var script = document.createElement("script");
script.src = "http://www.example.com/test.php?name=LiLei&age=28";
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即可实现双方互相访问和修改,如同没有跨域一样,但是要注意不能改为其它域:
document.domain = "example.com"; // 成功
document.domain = "baidu.com"; // 报错
不能将主域修改为子域
如下,位于p2p.example.com的页面:
document.domain = "example.com"; // 成功
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
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>A</title>
<body>
<p>A</p>
<input type="button" value="发送消息到frameB并得到frameB的回应" onclick="sendMsg()">
<iframe id="frameB" src="./B.html" style="width: 90%;"></iframe>
<script type="text/javascript">
window.addEventListener("message", function (event) {
console.log("A.html接收到消息:" + event.data);
}); function sendMsg () {
var frameB = document.getElementById("frameB");
frameB.contentWindow.postMessage("Hello I am Li Lei", "*");
}
</script>
</body>
</html>
B.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>B</title>
</head>
<body bgcolor="#ff0000">
<p>B</p>
<script type="text/javascript">
window.addEventListener("message", function (event) {
console.log("B.html接收到消息:" + event.data);
//向发送消息的window回送消息
event.source.postMessage("Hi, I am Han Meimei!", "*");
});
</script>
</body>
</html>
需要注意,window对象发送的消息只有自身可以接收到message消息。
crossOrigin属性
服务端需要配置各种类型资源的跨域范围权限,如Apache服务器可以如下配置:
<IfModule mod_setenvif.c>
<IfModule mod_headers.c>
<FilesMatch "\.(cur|gif|ico|jpe?g|png|svgz?|webp)$">
SetEnvIf Origin ":" IS_CORS
Header set Access-Control-Allow-Origin "*" env=IS_CORS
</FilesMatch>
</IfModule>
</IfModule>
Node.js如下配置:
app.all('*',function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
if (req.method == 'OPTIONS') {
res.send(200); /让options请求快速返回/
} else {
next();
}
});
crossOrigin属性的值
该枚举属性指定在加载相关图片时是否必须使用CORS。
- 当不设置该属性时,资源将会不使用CORS加载(即不发送Origin:HTTP头),这将阻止其在元素中进行使用。若设置了非法的值,则视为使用anonymous。
- "anonymous":会发起一个跨域请求(即包含Origin: HTTP头)。但不会发送任何认证信息(即不发送cookie, X.509证书和HTTP基本认证信息)。如果服务器没有给出源站凭证(不设置Access-Control-Allow-Origin: HTTP头),这张图片就会被污染并限制使用。
- "use-credentials":会发起一个带有认证信息(发送cookie,X.509证书和HTTP基本认证信息)的跨域请求(即包含Origin:HTTP头)。如果服务器没有给出源站凭证(不设置Access-Control-Allow-Origin: HTTP头),这张图片就会被污染并限制使用。
script标签
当我们引入一个不同源的js代码时,不设置crossOrigin属性,在执行上是没有问题的,报错在console面板也可以看到堆栈信息,但是在当前页面使用window.onerror来抓取错误时,只能得到一个Script error的信息,没有更多的信息了。
当我们为script标签添加crossorigin="anonymous"属性后,如果服务端没有对跨域进行设置,会报如下错误:
Access to Script at 'http://127.0.0.1:3000/test.js' from origin 'http://192.168.2.34' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://192.168.2.34' is therefore not allowed access.
服务端进行允许跨域设定后,window.onerror可以抓取到详细的信息了。
img标签
我们在请求其它域下的图片时,可以显示出来,但是当调用toBlob(),toDataURL()和getImageData()等方法时会抛出安全错误,这是由于浏览器的安全策略导致的。
我们先看看下面的代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<canvas id='canvas' width='500px' height='500px'></canvas> <script>
var image = document.createElement("img");
// image.crossOrigin = "anonymous";
image.onload = function() {
drawImg(image);
};
image.onerror = function() {
console.log("load image error!");
};
image.src = "http://127.0.0.1:3000/images/img.png"; function drawImg(img) {
var canvas = document.getElementById('canvas');
var context = canvas.getContext("2d");
context.drawImage(img, 0, 0, 100, 100); var base64 = canvas.toDataURL('image/png');
console.log(base64);
}
</script>
</body>
</html>
图片不在当前域,可以绘制到canvas里显示,但是调用toDataURL时会出现跨域的报错,解决方法就是去掉注释,为img标签添加crossOrigin="anonymous"的属性。
添加后,也需要服务端进行允许跨域设定才行哦,刷新后可以得到canvas中的图片编码信息,服务端不设定跨域会得到onerror的事件并且打印跨域异常信息。
HTML5学习笔记(二十八):跨域的更多相关文章
- Java学习笔记二十八:Java中的接口
Java中的接口 一:Java的接口: 接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明.一个类通过继承接口的方式,从而来继承 ...
- Java基础学习笔记二十八 管家婆综合项目
本项目为JAVA基础综合项目,主要包括: 熟练View层.Service层.Dao层之间的方法相互调用操作.熟练dbutils操作数据库表完成增删改查. 项目功能分析 查询账务 多条件组合查询账务 添 ...
- angular学习笔记(二十八-附2)-$http,$resource中的promise对象
下面这种promise的用法,我从第一篇$http笔记到$resource笔记中,一直都有用到: HttpREST.factory('cardResource',function($resource) ...
- angular学习笔记(二十八-附1)-$resource中的资源的方法
通过$resource获取到的资源,或者是通过$resource实例化的资源,资源本身就拥有了一些方法,$save,$delete,$remove,可以直接调用来保存该资源: 比如有一个$resour ...
- angular学习笔记(二十八)-$http(6)-使用ngResource模块构建RESTful架构
ngResource模块是angular专门为RESTful架构而设计的一个模块,它提供了'$resource'模块,$resource模块是基于$http的一个封装.下面来看看它的详细用法 1.引入 ...
- PHP学习笔记二十八【抽象类】
<?php //定义一个抽象类.主要用来被继承 //如果一个类继承了抽象类,则它必须实现该抽象类的所有抽象方法(除非它自己也是抽象类) // abstract class Animal{ pub ...
- HTML5学习笔记(十八):闭包
高阶函数 JavaScript的函数其实都指向某个变量.既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,也可以返回一个函数,这种函数就称之为高阶函数. 函数作为参 ...
- Java框架spring 学习笔记(十八):事务管理(xml配置文件管理)
在Java框架spring 学习笔记(十八):事务操作中,有一个问题: package cn.service; import cn.dao.OrderDao; public class OrderSe ...
- python3.4学习笔记(二十六) Python 输出json到文件,让json.dumps输出中文 实例代码
python3.4学习笔记(二十六) Python 输出json到文件,让json.dumps输出中文 实例代码 python的json.dumps方法默认会输出成这种格式"\u535a\u ...
- python3.4学习笔记(二十五) Python 调用mysql redis实例代码
python3.4学习笔记(二十五) Python 调用mysql redis实例代码 #coding: utf-8 __author__ = 'zdz8207' #python2.7 import ...
随机推荐
- 6-16 单词 uva10129
了解了欧拉回路和欧拉道路的性质 : 欧拉道路 最多只有两个奇点(不可能只有一个奇点) 并且当有两个奇点的时候 一个奇点入比出多一 一个奇点出比入多一 采用并查集查看是否连同 如果连 ...
- 开发一个支持多用户同时在线的FTP程序
FTP 要求: .用户加密认证 .允许同时多用户登录 .每个用户有自己的家目录,且只能访问自己的家目录 .对用户进行磁盘配额,每个用户的可用空间不同 .允许用户在ftp server上随意切换目录 . ...
- 050 sqoop的使用
一:导入 mysql-->hdfs 1.准备 2.导入数据 可以看到在跑yarn. 3.在HDFS上看结果 默认的地址:hdfs的家目录. 4.在HDFS上指定目录 5.指定map的个数,相同 ...
- 潭州课堂25班:Ph201805201 WEB 之 JS 第四课 (课堂笔记)
JS 引入方式 在 HTML 中写入 写在 的标签里 <script> </script>推荐 放在 </body> 结束之前 <!DOCTYPE html& ...
- 解决boostrap中,iframe渲染下,苹果手机横向无法显示剩余内容问题
描述: 问题解决了,采用的手势拖动显示剩余内容,并不是有了横向滚动条 在head标签中加入 <head> <meta charset="utf-8"> &l ...
- Objective-C学习笔记(三)——用Objective-C编写第一个程序:Hello,World!
不管是哪一个程序猿,或者是学习哪一门计算机语言.写的第一个程序基本上就是Hello World. 今天我们用OC来实现第一个程序:Hello World. 在Xcode中选择新建一个项目,在对话框中选 ...
- Unity3D光照前置知识——Rendering Paths(渲染路径)及LightMode(光照模式)译解
简述 Unity supports different Rendering Paths. You should choose which one you use depending on your g ...
- 报错:bash: pip: command not found
$ wget https://bootstrap.pypa.io/get-pip.py$ python get-pip.py$ pip -V #查看pip版本
- shell自动补全功能:bash和zsh
首要一点:shell有多种,比如bash.zsh.csh.ksh.sh.tcsh等 因此,制作自动补全功能时,要先搞清楚,你使用的是哪种shell,各个shell制作方法是不同的,网上大部分介绍的是关 ...
- Netty重要概念介绍
Netty重要概念介绍 Bootstrap Netty应用程序通过设置bootstrap(引导)类开始,该类提供了一个用于网络成配置的容器. 一种是用于客户端的Bootstrap 一种是用于服务端的S ...