JS 跨域问题常见的五种解决方式
JS 跨域问题常见的五种解决方式
一、什么是跨域?
要理解跨域问题,就先理解好概念。跨域问题是由于javascript语言安全限制中的同源策略造成的.
简单来说,同源策略是指一段脚本只能读取来自同一来源的窗口和文档的属性,这里的同一来源指的是主机名、协议和端口号的组合.
URL 说明 是否允许通信
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 不同域名 不允许
同源策略设计之初是为了安全,但也对正常的跨域开发造成了一定影响,不过还是有不同的解决办法的。
二、解决办法
跨域问题,更多的情况是出现在需要用ajax获取数据时,那么现在就先看个非跨域的栗子
(功能主要是从后台获取names列表,并展示出来)
前端部分:
<body><div id="box"><ul>names:</ul></div><script type="text/javascript" src="./js/jquery.min.js"></script><script type="text/javascript">function addContents(data){ var box = document.getElementById('box'),ul = box.getElementsByTagName('ul')[0],fragment = document.createDocumentFragment(),li;for(var i=0,j=data.length; i<j; i++){ li = document.createElement('li');if(data[i].hasOwnProperty('name')){ li.appendChild(document.createTextNode(data[i].name));fragment.appendChild(li);}}ul.appendChild(fragment);}$.ajax({ url: './cross_domain.php',//url: 'http://demoff.sinaapp.com/cross_domain.php',type: 'GET',dataType: 'json',data: {},success: function(data){ addContents(data);},error: function(xmlHttpRequest,textStatus,error){ console.log(xmlHttpRequest.status);console.log(textStatus);}});</script>
</body>
现在后端php是设在同域之下:
<?php // 接收数据// $jsoncallback = $_GET["jsoncallback"];// 构造数据for($i=1; $i<=5; $i++){ $names[] = array("name" => "name" + $i);}// $data = $jsoncallback . "(" . json_encode($names) . ")";$data = json_encode($names);echo $data;
?>
ok, 这样一来数据可以正常加载,形如:
现在设置为跨域:将ajax请求部分的url域设为 http://demoff.sinaapp.com 即对换注释部分,就会产生跨域问题
好那就进行解决吧
第一: 使用 跨域资源共享(CORS)
CORS(Cross-Origin Resource Sharing
)跨域资源共享,定义了必须在访问跨域资源时,浏览器与服务器应该如何沟通。CORS
背后的基本思想就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。
使用方法也很简单,在php后端设置 Access-Control-Allow-Origin 头即可,如:
<?php header("Access-Control-Allow-Origin: *");//header("Access-Control-Allow-Origin: 我的域或ip");// 接收数据// $jsoncallback = $_GET["jsoncallback"];// 构造数据for($i=1; $i<=5; $i++){ $names[] = array("name" => "name" + $i);}// $data = $jsoncallback . "(" . json_encode($names) . ")";$data = json_encode($names);echo $data;
?>
第二:使用jsonp
什么是jsonp
?维基百科的定义是:JSONP(JSON with Padding).
JSONP
也叫填充式JSON,是应用JSON的一种新方法,只不过是被包含在函数调用中的JSON,例如:callback({"name","name1"});
JSONP由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数,而数据就是传入回调函数中的JSON数据。
jsonp的原理是:
就是利用<script>标签没有跨域限制,来达到与第三方通讯的目的。
当需要通讯时,本站脚本创建一个<script>元素,地址指向第三方的API网址,并提供一个回调函数来接收数据(函数名可约定,或通过地址参数传递)。
第三方产生的响应为json数据的包装(故称之为jsonp,即json padding),形如:
callback({"name":"hax","gender":"Male"})
这样浏览器会调用callback函数,并传递解析后json对象作为参数。本站脚本可在callback函数里处理所传入的数据。
(我们知道 <link href <img src <script src 请求的数据都不受域的限制)
jsonp的使用方法:
客户端指明使用jsonp的方式,服务器接受参数,并外包裹要返回的数据,再一并返回。
jquery的ajax简单描述:
前端指明data:jsonp , 在标明自定义的参数名 jsonp:jsoncallback
<body><div id="box"><ul>names:</ul></div><script type="text/javascript" src="./js/jquery.min.js"></script><script type="text/javascript">function addContents(data){ var box = document.getElementById('box'),ul = box.getElementsByTagName('ul')[0],fragment = document.createDocumentFragment(),li;for(var i=0,j=data.length; i<j; i++){ li = document.createElement('li');if(data[i].hasOwnProperty('name')){ li.appendChild(document.createTextNode(data[i].name));fragment.appendChild(li);}}ul.appendChild(fragment);}$.ajax({ //url: './cross_domain.php',url: 'http://demoff.sinaapp.com/cross_domain.php',type: 'GET',dataType: 'jsonp',jsonp: 'jsoncallback',data: {},success: function(data){ addContents(data);},error: function(xmlHttpRequest,textStatus,error){ console.log(xmlHttpRequest.status);console.log(textStatus);}});</script>
</body>
后端服务器部分要做的就是,拿到参数,再包裹
<?php //header("Access-Control-Allow-Origin: *");//header("Access-Control-Allow-Origin: 我的域或ip");// 接收数据$jsoncallback = $_GET["jsoncallback"];// 构造数据for($i=1; $i<=5; $i++){ $names[] = array("name" => "name" + $i);}$data = $jsoncallback . "(" . json_encode($names) . ")";//$data = json_encode($names);echo $data;
?>
结果显示:
你可能会奇怪这一大串是什么,这其实是jq自动生成的一个函数名(也就是那个jsoncallback参数的值)
其实还有一种很常见的方式就是使用 $.getJson获取,直接给出一个网址
把$.ajax部分替换成$.getJson部分
$.getJSON('http://demoff.sinaapp.com/cross_domain.php?jsoncallback=?',function(data){ addContents(data);});
jquery
会自动生成一个全局函数来替换callback=?
中的问号,之后获取到数据后又会自动销毁,实际上就是起一个临时代理函数的作用。$.getJSON
方法会自动判断是否跨域,不跨域的话,就调用普通的ajax
方法;跨域的话,则会以异步加载js文件的形式来调用jsonp
的回调函数。
我也可以指定那个值,因为我们目的是要运行addContents函数,那就可以直接指定为它。不过这时就不能使用$.getJson版的匿名函数了
直接再加个<script> 看看结果,数据返回后相应的函数就被调用执行。
<script type="text/javascript" src="http://demoff.sinaapp.com/cross_domain.php?jsoncallback=addContents"></script>
jsonp的方式很简便,它的缺点就是:
它只支持GET请求而不支持POST等其它类型的HTTP请求;
它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript
调用的问题。
第三: document.domain + iframe (iframe的使用主要是为了ajax通信)
不同的框架之间是可以获取window对象的,但却无法获取相应的属性和方法。
比如,有一个页面,它的地址是http://www.example.com/a.html
,
在这个页面里面有一个iframe
,它的src是http://example.com/b.html
,
很显然,这个页面与它里面的iframe
框架是不同域的,所以我们是无法通过在页面中书写js代码来获取iframe
中的东西的:
<script type="text/javascript">function test(){var iframe = document.getElementById('ifame');var win = document.contentWindow;//可以获取到iframe里的window对象,但该window对象的属性和方法几乎是不可用的var doc = win.document;//这里获取不到iframe里的document对象var name = win.name;//这里同样获取不到window对象的name属性}
</script>
<iframe id = "iframe" src="http://example.com/b.html" onload = "test()"></iframe>
这个时候,document.domain
就可以派上用场了,
我们只要把http://www.example.com/a.html
和http://example.com/b.html
这两个页面的document.domain都设成相同的域名就可以了。
但要注意的是,document.domain的设置是有限制的,我们只能把document.domain设置成自身或更高一级的父域,且主域必须相同。
1.在页面 http://www.example.com/a.html
中设置document.domain
:
<iframe id = "iframe" src="http://example.com/b.html" onload = "test()"></iframe>
<script type="text/javascript">document.domain = 'example.com';//设置成主域function test(){alert(document.getElementById('iframe').contentWindow);//contentWindow 可取得子窗口的 window 对象}
</script>
2.在页面 http://example.com/b.html
中也设置document.domain
:
<script type="text/javascript">document.domain = 'example.com';//在iframe载入这个页面也设置document.domain,使之与主页面的document.domain相同
</script>
上述只谈到了,document.domain ,主要是为了不同域间访问数据操作数据。
如果想在
http://www.example.com/a.html
页面中通过ajax直接请求下述的页面,可以用一个隐藏的iframe来做一个代理。
http://example.com/b.html
原理就是让这个iframe载入一个与你想要通过ajax获取数据的目标页面处在相同的域的页面,所以这个iframe中的页面是可以正常使用ajax去获取你要的数据的,然后就是通过我们刚刚讲得修改document.domain的方法,让我们能通过js完全控制这个iframe,这样我们就可以让iframe去发送ajax请求,然后收到的数据我们也可以获得了。
第四: 使用window.name + iframe
window
对象有个name
属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name
的,每个页面对window.name
都有读写的权限,window.name
是持久存在一个窗口载入过的所有页面中的.
(简单来看,window作为浏览器端的全局对象,默认可不加,所以 也可以简单地直接用name代替
但name也不是简单地充当全局变量使用。所以要注意的是,只能使用name这个属性,使用诸如 window.name_1之类的是不行的
我现在使用var name= 就隐式地声明window.name了)
比如现在有两个不同域的a.html和b.html
http://localhost:8080/demoff/a.html
<script type="text/javascript">var name = 'myNames';setTimeout(function(){ location = 'http://demoff.sinaapp.com/b.html';},3000);</script>
http://demoff.sinaapp.com/b.html
<script type="text/javascript">alert(name);</script>
3秒后跳转过去可以看到 :
数据是存在的,但实际情况中我们也不能这样跳来跳去,所以可以用隐藏的iframe来实现数据的获取
举个荔枝:
本地的为数据提供方:http://localhost:8080/demoff/b.html
远程的为数据需求方:http://demoff.sinaapp.com/b.html
则本地b.html文件:
<body><script type="text/javascript">window.name = 'myName';</script>
</body>
远程b.html文件
<body><!-- 一个 iframe 作为中间件 --><iframe id="myIframe" src="http://localhost:8080/demoff/a.html" style="display:none;" onload="getData()"></iframe><script type="text/javascript">// 初始iframe加载后即执行function getData(){ var myIframe = document.getElementById('myIframe');// 第一次iframe加载后即可拿到window.name值,然后设置其src为同域的文件(随意)myIframe.src = './index.php';// 设置好同域之后,就可以操作iframe的数据了,即可拿到该name值myIframe.onload = function(){ var name = myIframe.contentWindow.name;alert(name);};}</script>
</body>
即可拿到数据
第五:使用 window.postMessage方法
这个东西是HTML5引入的,可以在不同的window下传递数据,不受域的影响。目前IE8+、FireFox、Chrome、Opera等浏览器都已经支持该方法。
window.postMessage(message,targetOrigin)
调用postMessage方法的window对象是指要接收消息的那一个window对象,该方法的第一个参数message为要发送的消息,类型只能为字符串;
第二个参数targetOrigin用来限定接收消息的那个window对象所在的域,如果不想限定域,可以使用通配符 * 。
需要接收消息的window对象,可是通过监听自身的message事件来获取传过来的消息,消息内容储存在该事件对象的data属性中。
还是举个栗子:(假设现在是远程提供数据,本地获取数据)
远程b.html
<body><!-- 一个 iframe 作为中间件 --><iframe id="myIframe" src="http://localhost:8080/demoff/a.html" style="display:none;" onload="setData()"></iframe><script type="text/javascript">// 初始iframe加载后即执行function setData(){ var myIframe = document.getElementById('myIframe');var win = myIframe.contentWindow;//win.postMessage('My name is null','*');win.postMessage('My name is null','http://localhost:8080/');}</script>
</body>
本地b.html
<body><script type="text/javascript">window.onmessage = function(e){ e = e || window.event;alert(e.data);};</script>
</body>
本地即可获取到数据
JS 跨域问题常见的五种解决方式相关推荐
- 跨域问题常用的三种解决方式
一.什么是跨域?跨域是如何产生的? 同源策略: 浏览器内置的规则!是浏览器提供的一种安全机制,限制来自不同源的数据.如果当前页面的URL地址和Ajax的请求地址不同源,浏览器不允许得到服务器的数据: ...
- 跨域问题详解——九种解决跨域方法
跨域是前端再常见不过的问题了,下面主要针对跨域做一次总结,一次理清楚. 一.jsonp解决跨域 jsonp解决跨域问题的原理是:script不受同源策略的影响. //前端代码: <!DOCTYP ...
- jsonp-反向代理-CORS解决JS跨域问题的个人总结
jsonp-反向代理-CORS解决JS跨域问题的个人总结 网上说了很多很多,但是看完之后还是很混乱,所以我自己重新总结一下. 解决 js 跨域问题一共有8种方法, jsonp(只支持 get) 反向代 ...
- 5种处理js跨域问题方法汇总(转载)
1.JSONP跨域GET请求 ajax请求,dataType为jsonp.这种形式需要请求在服务端调整为返回callback([json-object])的形式.如果服务端返回的是普通json对象.那 ...
- 常见js跨域解决方案
以下为几种常见js跨域解决方案: ajax跨域请求 一.使用jsonp方式实现跨域请求 $.ajax({async: false, type:"POST",dataType: 'j ...
- js跨域解决方案php,详解js跨域原理以及2种解决方案_javascript技巧
1.什么是跨域 我们经常会在页面上使用ajax请求访问其他服务器的数据,此时,客户端会出现跨域问题. 跨域问题是由于javascript语言安全限制中的同源策略造成的. 简单来说,同源策略是指一段脚本 ...
- 前端面试查漏补缺--(三) 跨域及常见解决办法
前言 本系列最开始是为了自己面试准备的.后来发现整理越来越多,差不多有十二万字符,最后决定还是分享出来给大家. 为了分享整理出来,花费了自己大量的时间,起码是只自己用的三倍时间.如果喜欢的话,欢迎收藏 ...
- firefox如何载入json文件_如何去解决JS跨域问题 怎么能学好Web前端开发
如何去解决JS跨域问题?怎么能学好Web前端开发?JavaScript跨域是指通过JS在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过JS获取页面中不同域的框架中(if ...
- chrome浏览器的跨域设置 Google Chrome浏览器下开启禁用缓存和js跨域限制--disable-web-security...
chrome用户默认路径 Win7:C:\Users\[用户名]\AppData\Local\Google\Chrome\User Data\ XP:C:\Documents and Settings ...
最新文章
- 算法导论 第二部分——排序和顺序统计量
- python怎么打开交互式窗口-如何在交互式环境中执行Python程序
- php7下安装event扩展
- 【android-tips】如何在view中取得activity对象
- 浅谈Java中的Set、List、Map的区别
- odoo中的序列(自动增长的序列以及有条件的序列,ir.sequence)
- Metro App中使用Timer
- 论林耐斯-Linux系统的重要性
- 【转载】wpf数据绑定binding与INotifyPropertyChanged
- 【clickhouse】ClickHouseException code: 225 ZooKeeper session has been expired
- python工具的功能介绍_Python功能工具
- Win装linux双系统教程
- YUI3 Overlay的使用
- protobuf静态库隐藏符号的坑
- 3DMax如何通过光子图渲染
- 部署Unbound实现DNS服务
- 宏正ATEN发行全新高端式IP-Based Cat 5 KVM多电脑切换器
- 市场调研很难做?这些软件帮你理清思绪
- 如何快速的下载百度网盘的文件
- 支持HEVC/H265 RTMP播放的VLC WINDOWS版本
热门文章
- 跳出以人为中心,从事情发展的角度看问题本质
- 二元牛顿迭代法matlab,牛顿迭代法解二元方程组以及误差分析 matlab实现
- access中所有女生的记录,ACCESS建查询,如何查两个表里的内容
- [附源码]计算机毕业设计Node.js志远奶茶网络设计(程序+LW)
- html怎么设置过渡字体,web前端入门到实战:css3基础-文本与字体+转换+过渡+动画+案例...
- 手工纸盒子_手工折纸迷你抽纸盒步骤详细教程
- Android安卓扫名片识别内容技术SDK
- 等保建设整改_等保三级必备网络安全设备
- ios keychain 不被清理_清理垃圾,恢复出厂设置真的能让手机变快吗?
- 计算机网络之HTTP协议