js中几种实用的跨域方法原理详解

这里说的js跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同域的框架中(iframe)的数据。只要协议、域名、端口有任何一个不同,都被当作是不同的域。

下表给出了相对http://store.company.com/dir/page.html同源检测的结果:

QQ截图20130613230631

要解决跨域的问题,我们可以使用以下几种方法:

一、通过jsonp跨域

在js中,我们直接用XMLHttpRequest请求不同域上的数据时,是不可以的。但是,在页面上引入不同域上的js脚本文件却是可以的,jsonp正是利用这个特性来实现的。

比如,有个a.html页面,它里面的代码需要利用ajax获取一个不同域上的json数据,假设这个json数据地址是http://example.com/data.php,那么a.html中的代码就可以这样:

QQ截图20130613230631

我们看到获取数据的地址后面还有一个callback参数,按惯例是用这个参数名,但是你用其他的也一样。当然如果获取数据的jsonp地址页面不是你自己能控制的,就得按照提供数据的那一方的规定格式来操作了。

因为是当做一个js文件来引入的,所以http://example.com/data.php返回的必须是一个能执行的js文件,所以这个页面的php代码可能是这样的:

QQ截图20130613230631

最终那个页面输出的结果是:

QQ截图20130613230631

所以通过http://example.com/data.php?callback=dosomething得到的js文件,就是我们之前定义的dosomething函数,并且它的参数就是我们需要的json数据,这样我们就跨域获得了我们需要的数据。

这样jsonp的原理就很清楚了,通过script标签引入一个js文件,这个js文件载入成功后会执行我们在url参数中指定的函数,并且会把我们需要的json数据作为参数传入。所以jsonp是需要服务器端的页面进行相应的配合的。

知道jsonp跨域的原理后我们就可以用js动态生成script标签来进行跨域操作了,而不用特意的手动的书写那些script标签。如果你的页面使用jquery,那么通过它封装的方法就能很方便的来进行jsonp操作了。

QQ截图20130613230631

原理是一样的,只不过我们不需要手动的插入script标签以及定义回掉函数。jquery会自动生成一个全局函数来替换callback=?中的问号,之后获取到数据后又会自动销毁,实际上就是起一个临时代理函数的作用。$.getJSON方法会自动判断是否跨域,不跨域的话,就调用普通的ajax方法;跨域的话,则会以异步加载js文件的形式来调用jsonp的回调函数。

 

2、通过修改document.domain来跨子域

浏览器都有一个同源策略,其限制之一就是第一种方法中我们说的不能通过ajax的方法去请求不同源中的文档。 它的第二个限制是浏览器中不同域的框架之间是不能进行js的交互操作的。有一点需要说明,不同的框架之间(父子或同辈),是能够获取到彼此的window对象的,但蛋疼的是你却不能使用获取到的window对象的属性和方法(html5中的postMessage方法是一个例外,还有些浏览器比如ie6也可以使用top、parent等少数几个属性),总之,你可以当做是只能获取到一个几乎无用的window对象。比如,有一个页面,它的地址是http://www.example.com/a.html  , 在这个页面里面有一个iframe,它的src是http://example.com/b.html, 很显然,这个页面与它里面的iframe框架是不同域的,所以我们是无法通过在页面中书写js代码来获取iframe中的东西的:

  QQ截图20130613230631

这个时候,document.domain就可以派上用场了,我们只要把http://www.example.com/a.html 和 http://example.com/b.html这两个页面的document.domain都设成相同的域名就可以了。但要注意的是,document.domain的设置是有限制的,我们只能把document.domain设置成自身或更高一级的父域,且主域必须相同。例如:a.b.example.com 中某个文档的document.domain 可以设成a.b.example.com、b.example.com 、example.com中的任意一个,但是不可以设成 c.a.b.example.com,因为这是当前域的子域,也不可以设成baidu.com,因为主域已经不相同了。

在页面 http://www.example.com/a.html 中设置document.domain:

QQ截图20130613230631

在页面 http://example.com/b.html 中也设置document.domain,而且这也是必须的,虽然这个文档的domain就是example.com,但是还是必须显示的设置document.domain的值:

QQ截图20130613230631

这样我们就可以通过js访问到iframe中的各种属性和对象了。

不过如果你想在http://www.example.com/a.html 页面中通过ajax直接请求http://example.com/b.html 页面,即使你设置了相同的document.domain也还是不行的,所以修改document.domain的方法只适用于不同子域的框架间的交互。如果你想通过ajax的方法去与不同子域的页面交互,除了使用jsonp的方法外,还可以用一个隐藏的iframe来做一个代理。原理就是让这个iframe载入一个与你想要通过ajax获取数据的目标页面处在相同的域的页面,所以这个iframe中的页面是可以正常使用ajax去获取你要的数据的,然后就是通过我们刚刚讲得修改document.domain的方法,让我们能通过js完全控制这个iframe,这样我们就可以让iframe去发送ajax请求,然后收到的数据我们也可以获得了。

 

3、使用window.name来进行跨域

window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。

比如:有一个页面a.html,它里面有这样的代码:

QQ截图20130613230631

再看看b.html页面的代码:

QQ截图20130613230631

a.html页面载入后3秒,跳转到了b.html页面,结果为:

QQ截图20130613230631

我们看到在b.html页面上成功获取到了它的上一个页面a.html给window.name设置的值。如果在之后所有载入的页面都没对window.name进行修改的话,那么所有这些页面获取到的window.name的值都是a.html页面设置的那个值。当然,如果有需要,其中的任何一个页面都可以对window.name的值进行修改。注意,window.name的值只能是字符串的形式,这个字符串的大小最大能允许2M左右甚至更大的一个容量,具体取决于不同的浏览器,但一般是够用了。

上面的例子中,我们用到的页面a.html和b.html是处于同一个域的,但是即使a.html与b.html处于不同的域中,上述结论同样是适用的,这也正是利用window.name进行跨域的原理。

下面就来看一看具体是怎么样通过window.name来跨域获取数据的。还是举例说明。

比如有一个www.example.com/a.html页面,需要通过a.html页面里的js来获取另一个位于不同域上的页面www.cnblogs.com/data.html里的数据。

data.html页面里的代码很简单,就是给当前的window.name设置一个a.html页面想要得到的数据值。data.html里的代码:

QQ截图20130613230631

那么在a.html页面中,我们怎么把data.html页面载入进来呢?显然我们不能直接在a.html页面中通过改变window.location来载入data.html页面,因为我们想要即使a.html页面不跳转也能得到data.html里的数据。答案就是在a.html页面中使用一个隐藏的iframe来充当一个中间人角色,由iframe去获取data.html的数据,然后a.html再去得到iframe获取到的数据。

充当中间人的iframe想要获取到data.html的通过window.name设置的数据,只需要把这个iframe的src设为www.cnblogs.com/data.html就行了。然后a.html想要得到iframe所获取到的数据,也就是想要得到iframe的window.name的值,还必须把这个iframe的src设成跟a.html页面同一个域才行,不然根据前面讲的同源策略,a.html是不能访问到iframe里的window.name属性的。这就是整个跨域过程。

看下a.html页面的代码:

上面的代码只是最简单的原理演示代码,你可以对使用js封装上面的过程,比如动态的创建iframe,动态的注册各种事件等等,当然为了安全,获取完数据后,还可以销毁作为代理的iframe。网上也有很多类似的现成代码,有兴趣的可以去找一下。

通过window.name来进行跨域,就是这样子的。

 

4、使用HTML5中新引进的window.postMessage方法来跨域传送数据

window.postMessage(message,targetOrigin)  方法是html5新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源,目前IE8+、FireFox、Chrome、Opera等浏览器都已经支持window.postMessage方法。

调用postMessage方法的window对象是指要接收消息的那一个window对象,该方法的第一个参数message为要发送的消息,类型只能为字符串;第二个参数targetOrigin用来限定接收消息的那个window对象所在的域,如果不想限定域,可以使用通配符 *  。

需要接收消息的window对象,可是通过监听自身的message事件来获取传过来的消息,消息内容储存在该事件对象的data属性中。

上面所说的向其他window对象发送消息,其实就是指一个页面有几个框架的那种情况,因为每一个框架都有一个window对象。在讨论第二种方法的时候,我们说过,不同域的框架间是可以获取到对方的window对象的,而且也可以使用window.postMessage这个方法。下面看一个简单的示例,有两个页面

QQ截图20130613230631

 

QQ截图20130613230631

我们运行a页面后得到的结果:

QQ截图20130613230631

我们看到b页面成功的收到了消息。

使用postMessage来跨域传送数据还是比较直观和方便的,但是缺点是IE6、IE7不支持,所以用不用还得根据实际需要来决定。

 

结语:

除了以上几种方法外,还有flash、在服务器上设置代理页面等跨域方式,这里就不做介绍了。

以上四种方法,可以根据项目的实际情况来进行选择应用,个人认为window.name的方法既不复杂,也能兼容到几乎所有浏览器,这真是极好的一种跨域方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/352121.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python中matplotlib画图_Python-matplotlib画图(莫烦笔记)

这个是我对于莫烦老师的matplotlib模块的视频做的一个笔记。1.前言Matplotlib是一个python的 2D绘图库,它以各种硬拷贝格式和跨平台的交互式环境生成出版质量级别的图形。通过Matplotlib,开发者可以仅需要几行代码,便可以生成绘图&#xff0c…

js词法分析

javascript词法分析函数在运行的瞬间,生成一个活动对象(Active Object),简称AO;具体分为两个阶段:一.分析阶段JavaScript代码运行前有一个类似编译的过程即词法分析,词法分析主要有三个步骤&…

如何使一维数组一行一行的输出成二维数组的格式

以9个元素的数组为例,输出3x3的格式 基本思想是对每一个元素的index进行检查,当index是3,6,9等可以被3整除的数时,在打印它们之前,先打印一个\n。注意0也可以被3整除,但是此时不打印\n,所以要加上i>0的…

python我想对你说_python学习第3天-----字典、解构

1.字典1)定义:查找速度快,效率高;用{}括起来,内部使用key:value的形式来保存数据;键值对是无序的,不是按照定义的方式保存数据的(类似于json文件),例如: {jay:周杰伦,jj::林俊杰}注&…

bms_output.put_line使用方法

https://blog.csdn.net/sxww321/article/details/4020300转载于:https://www.cnblogs.com/diyunpeng/p/10022923.html

linux 权限掩码 umask

一 权限掩码umaskumask是chmod配套的,总共为4位(gid/uid,属主,组权,其它用户的权限),不过通常用到的是后3个,例如你用chmod 755 file(此时这文件的权限是属主读(4)写(2)+执行(1),同…

ant 构建_有用的Ant构建标签

ant 构建问题: 如何在ant文件中执行以下任务? 制作zip文件。 运行命令。 将文件复制到远程计算机。 在远程Linux机器上运行命令。 打开输入框并响应输入值。 拨打蚂蚁电话。 答案: 1.制作zip文件: 以下是在ant中制作zip文…

C++文件交互(txt、excel)

前些天完成了计算机视觉大作业,《基于双目立体视觉的深度图像生成》,虽然刚开始觉得作业很难,也没找到现成的程序,但做到最后还是学到很多知识,get到很多技能,现在一一总结下来。 1、C输出数据到txt 包含…

基类数组存放派生类_永远不要将派生类数组赋值给基类类型指针

C.152: Never assign a pointer to an array of derived class objects to a pointer to its baseC.152:永远不要将派生类数组的指针赋值给基类指针Reason(原因)Subscripting the resulting base pointer will lead to invalid object access and probably to memory corruptio…

路由器和网关的区别?

1.网关实质上是一个网络通向其他网络的IP地址。比如有网络A和网络B,网络A的IP地址范围为“192.168.1.1~192. 168.1.254”,子网掩码为255.255.255.0;网络B的IP地址范围为“192.168.2.1~192.168.2.254”,子网掩码为255.2…

C++error:找不到指定文件

做作业遇到这样的问题:新建了一个C工程,在内部新建了a.cpp a.h文件,将另一个工程里面的内容复制了过来,但是运行显示“找不到指定文件”,找了一下,发现虽然我的项目源文件里包换这个cpp文件,但是…

MVC 1.0的可能方法

如《 Java EE 8积极路线图》中所述,Java EE 8计划中未包含MVC 1.0。 我的看法以及在JavaOne期间与我交谈过的几个人的指示,这样做的可能结果是: MVC已完全删除 MVC继续并包含在Java EE 8(JSR 366)中 MVC继续作为Ja…

ES2015 中的函数式Mixin

原文链接:http://raganwald.com/2015/06/17/functional-mixins.html 在“原型即对象”中,我们看到可以对原型使用 Object.assign 来模拟 mixin,原型是 JavaScript 中类概念的基石。现在我们将回顾这个概念,并进一步探究如何将功能…

spring中的设计模式_面试:设计模式在spring中的应用

设计模式为我们解决一类问题提供了最佳的解决方案,我们在实际工作其实不太常用到,以至于会经常想不到设计模式。究其原因都是我们只是在使用别人框架的缘故,在这些框架的代码中经常能看到设计模式的影子,我们以spring为例&#xf…

linux route命令的使用详解

route命令用于显示和操作IP路由表。要实现两个不同的子网之间的通信,需要一台连接两个网络的路由器,或者同时位于两个网络的网关来实现。在Linux系统中,设置路由通常是 为了解决以下问题:该Linux系统在一个局域网中,局…

C如何将二维数组作为返回值

做大作业遇到这样一个问题:在子函数里申请了一个二维数组,在主函数里要用到二维数组里的数据,但是在主函数里又不能提前申请(因为不知道数组长度),所以需要将数组return得到。 子函数: float **…

Spring的IOC底层实现

IOC的底层实现 续图: 转载于:https://www.cnblogs.com/phyger/p/10027712.html

python传文件给java_python使用简单http协议来传送文件

python使用简单http协议来传送文件!在ubuntu环境下,局域网内可以使用nc来传送文件,也可以使用基于Http协议的方式来下载文件我们可以使用python -m SimpleHTTPServer ${port}来启动服务默认的端口是8000,另外我们也可以指定端口&a…

C定义全局变量

程序工程中往往遇到这样的问题:某个变量是贯穿始终的,主函数以及不同的子函数都要用到这个变量,并且要调用子函数改变变量的值。这时候全局变量就起到一个桥梁作用,在函数外定义,在主函数中调用定义,在子函…

tensorflow(centos 7.0 64)安装

tensorflow安装 系统centos 7.0 64位: python版本:(注意tensorflow目前只支持python2.7版本) 安装pip yum update -y && yum install -y python python-devel epel-release.noarch python-pip 使用pip安装tensorflow pip install https://storag…