JS跨域(ajax跨域、iframe跨域)解决方法及原理详解(jsonp)

 

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

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

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

1、通过jsonp跨域

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

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

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

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

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

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

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

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

原理是一样的,只不过我们不需要手动的插入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中的东西的:

这个时候,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:

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

这样我们就可以通过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,它里面有这样的代码:

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

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

我们看到在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里的代码:

那么在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这个方法。下面看一个简单的示例,有两个页面

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

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

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

结语:

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

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

 

转载于:https://www.cnblogs.com/gscq073240/articles/8283664.html

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

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

相关文章

xenserver 安装新硬盘_给Xenserver添加新硬盘

首先我们进入到xenserver的Console界面.然后按下enter进入命令模式,接下来.咱们先看看硬盘有没有存在输入fdisk -l出现如下提示:Disk /dev/sda: 500.1 GB, 500107862016 bytes255 heads, 63 sectors/track, 60801 cylindersUnits cylinders of 16065 * 512 8225280 bytesDevi…

go-study

package (包) 一个目录下面所有的.go文件的包名必须相同. 包名一般和目录名相同(是约定, 不是强制), 包名都小写main包是一个特殊的包名, 在main包中, 必须包含func main()函数导入包(import)的时候, 使用的是包所在目录的路径, 路径中不用包含包的名字, 在使用包的时候,直接用…

什么是系统安全状态

指系统能按某种顺序如(P1,P2,...,Pn),来为每个进程分配所需要的资源,直至最大需求,使每个进程都可以顺序完成。若系统不存在这样一个安全序列,则称系统处于不安全状态。

SQL零基础学习笔记(一)

真的不知道我写了这么多不同的的学习笔记又没用。。开始SQL零基础学习笔记 百度百科:SQL(Structured Query Language)结构化查询语言,是一种数据库查询和程序设计语言,用于存取数据以及查询、更新和管理关系数据库系统。同时也是数…

WPF 列表虚拟化时的滚动方式

ListBox的滚动方式 分为像素滚动和列表项滚动 通过ListBox的附加属性ScrollViewer.CanContentScroll来设置。因此ListBox的默认模板中&#xff0c;含有ScrollViewer&#xff0c;ScrollViewer下存放列表内容 <ScrollViewer FocusVisualStyle"{x:Null}"><Item…

python爬虫文献综述_基于Python下的爬虫综述及应用

98 Internet Application 互联网 应用 引言&#xff1a;如今&#xff0c;大数据已经进入我们的各个领域&#xff0c;我们的工作及应用越来越需要获取大量的数据。我们可以想象在一张蜘蛛网上沿着我们所需的方向爬取猎物(数据)的感觉。实际上爬虫就是通过人为的模拟浏览器行为&…

HelloWorld新手常见问题

1.显示错误&#xff1a; HelloWorld.java:1: 错误: 写入HelloWorld时出错: C:\HelloWorld.class public class HelloWorld { ^ 1 个错误 解决方法&#xff1a; 将文件转移至D盘&#xff0c;然后编译即可。不要放在C盘&#xff01; 2.显示错误&#xff1a; 错误: 找不…

浅谈HTTPS以及Fiddler抓取HTTPS协议

原文 浅谈HTTPS以及Fiddler抓取HTTPS协议 最近想尝试基于Fiddler的录制功能做一些接口的获取和处理工作&#xff0c;碰到的一个问题就是简单连接Fiddler只能抓取HTTP协议&#xff0c;关键的登录请求等HTTPS协议都没有捕捉到&#xff0c;所以想让Fiddler能够同时抓取到HTTPS和HT…

关于.c和.h 和定义变量的问题

最初调试的时候是因为有个错误在wavplay.h文件中 于是我跳到了recorderl.h中:从图中看到引用了main.h 出现这个问题的具体原因还是不太清楚: 不过我任务是因为: wavplay.h中定义了 __WaveHeader 变量 在main.h文件中引用了wavplay.h 而在wavplay.h中试图引用main.h里的 __Wa…

halcon中面到面的距离_halcon学习笔记——(8)由标定板得到测量平面位姿-阿里云开发者社区...

如图&#xff1a;由标定板位姿获取测量板位姿1.pose_to_hom_mat3d( : : Pose : HomMat3D)把三维位姿转化为齐次变换矩阵2.hom_mat3d_translate_local( : : HomMat3D, Tx, Ty, Tz : HomMat3DTranslate)相对于新坐标系的平移变换/ 1 0 0 \ / Tx \HomMat3DTranslate HomMat3D *…

进程同步与互斥的区别

并发进程的执行会产生相互制约的关系&#xff1a;一种是进程之间竞争使用临界资源&#xff0c;只能让他们逐个使用&#xff0c;这种现象称为互斥&#xff0c;是一种竞争关系。另一种是进程之间协同完成任务&#xff0c;在关键点上等待另一进程发来的消息&#xff0c;以便协同一…

《软件调试分析技术》学习笔记

《软件调试分析技术》学习笔记&#xff08;一&#xff09; 今天开始写写一些心得体验。 《软件调试分析技术》是好友Monster的处女作品。作为一直以的好伙伴&#xff0c;他是我看着长大的&#xff0c;(*^__^*) 嘻嘻……之所以有今天这样的成绩&#xff0c;是与他的努力和天赋…

new Date()时间

var myDate new Date(); myDate.toLocaleDateString()&#xff1b;可以获取当前日期myDate.toLocaleTimeString(); 可以获取当前时间扩展&#xff1a;myDate.getYear(); //获取当前年份(2位)myDate.getFullYear(); //获取完整的年份(4位,1970-????)myDate.getMonth(); //获…

什么是对等网络

对等网也称工作组。在对等网络中&#xff0c;计算机的数量通常不会超过10台。所以对等网络相对比较简单。在对等网络中&#xff0c;对等网上各台计算机的有相同的功能&#xff0c;无主从之分&#xff0c;网上任一节台计算机既可以作为网络服务器&#xff0c;其资源为其它计算机…

python初中必背语法_一些python语法的合集

贴几道自己做过的py题防止忘了&#xff0c;好像包含的语法还挺多的洛谷1018nn,mminput().split( )oint(input())nint(nn);mint(mm)a[[]];c[[]]for i in range (1,43):a.append([]);c.append([])for i in range (1,43):for j in range (1,43):a[i].append(0),c[i].append(0)b[]f…

微信 {errcode:48001,errmsg:api unauthorized, hints: [ req_id: 1QoCla0699ns81 ]}

{"errcode":48001,"errmsg":"api unauthorized, hints: [ req_id: 1QoCla0699ns81 ]"} 声明&#xff1a;是已认证的服务号 这个问题已解决&#xff01;&#xff01; 前置说明 网页授权获取用户基本信息&#xff1a;两种 scope 域 https://open.w…

解决ubuntu首次安装Mysql之后,首次登录出现ERROR 1698 (28000): Access denied for user 'root'@'localhost'的方法

解决步骤&#xff1a; 1.打开终端&#xff0c;输入sudo vi /etc/mysql/debian.cnf 打开/etc/mysql/debian.cnf文件&#xff0c;显示如下&#xff1a; 2.mysql -udebian-sys-maint -p 打开mysql 输入密码为上图中password字段 3.修改root密码 ALTER USER rootlocalhost IDEN…

六十万的成长_我的EA策略分析和实现

曾经看过一个故事&#xff1a;在流水线上需要检测面包盒子是不是空的&#xff0c;科技人员使用各种红外线等等高科技&#xff0c;达到了目的&#xff1b;有一个工人用一台风扇就解决了这个问题......其实解决问题可以很简单&#xff0c;只要能实现目标&#xff0c;过程越简单越…