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 跨域问题常见的五种解决方式相关推荐

  1. 跨域问题常用的三种解决方式

    一.什么是跨域?跨域是如何产生的? 同源策略: 浏览器内置的规则!是浏览器提供的一种安全机制,限制来自不同源的数据.如果当前页面的URL地址和Ajax的请求地址不同源,浏览器不允许得到服务器的数据: ...

  2. 跨域问题详解——九种解决跨域方法

    跨域是前端再常见不过的问题了,下面主要针对跨域做一次总结,一次理清楚. 一.jsonp解决跨域 jsonp解决跨域问题的原理是:script不受同源策略的影响. //前端代码: <!DOCTYP ...

  3. jsonp-反向代理-CORS解决JS跨域问题的个人总结

    jsonp-反向代理-CORS解决JS跨域问题的个人总结 网上说了很多很多,但是看完之后还是很混乱,所以我自己重新总结一下. 解决 js 跨域问题一共有8种方法, jsonp(只支持 get) 反向代 ...

  4. 5种处理js跨域问题方法汇总(转载)

    1.JSONP跨域GET请求 ajax请求,dataType为jsonp.这种形式需要请求在服务端调整为返回callback([json-object])的形式.如果服务端返回的是普通json对象.那 ...

  5. 常见js跨域解决方案

    以下为几种常见js跨域解决方案: ajax跨域请求 一.使用jsonp方式实现跨域请求 $.ajax({async: false, type:"POST",dataType: 'j ...

  6. js跨域解决方案php,详解js跨域原理以及2种解决方案_javascript技巧

    1.什么是跨域 我们经常会在页面上使用ajax请求访问其他服务器的数据,此时,客户端会出现跨域问题. 跨域问题是由于javascript语言安全限制中的同源策略造成的. 简单来说,同源策略是指一段脚本 ...

  7. 前端面试查漏补缺--(三) 跨域及常见解决办法

    前言 本系列最开始是为了自己面试准备的.后来发现整理越来越多,差不多有十二万字符,最后决定还是分享出来给大家. 为了分享整理出来,花费了自己大量的时间,起码是只自己用的三倍时间.如果喜欢的话,欢迎收藏 ...

  8. firefox如何载入json文件_如何去解决JS跨域问题 怎么能学好Web前端开发

    如何去解决JS跨域问题?怎么能学好Web前端开发?JavaScript跨域是指通过JS在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过JS获取页面中不同域的框架中(if ...

  9. chrome浏览器的跨域设置 Google Chrome浏览器下开启禁用缓存和js跨域限制--disable-web-security...

    chrome用户默认路径 Win7:C:\Users\[用户名]\AppData\Local\Google\Chrome\User Data\ XP:C:\Documents and Settings ...

最新文章

  1. 算法导论 第二部分——排序和顺序统计量
  2. python怎么打开交互式窗口-如何在交互式环境中执行Python程序
  3. php7下安装event扩展
  4. 【android-tips】如何在view中取得activity对象
  5. 浅谈Java中的Set、List、Map的区别
  6. odoo中的序列(自动增长的序列以及有条件的序列,ir.sequence)
  7. Metro App中使用Timer
  8. 论林耐斯-Linux系统的重要性
  9. 【转载】wpf数据绑定binding与INotifyPropertyChanged
  10. 【clickhouse】ClickHouseException code: 225 ZooKeeper session has been expired
  11. python工具的功能介绍_Python功能工具
  12. Win装linux双系统教程
  13. YUI3 Overlay的使用
  14. protobuf静态库隐藏符号的坑
  15. 3DMax如何通过光子图渲染
  16. 部署Unbound实现DNS服务
  17. 宏正ATEN发行全新高端式IP-Based Cat 5 KVM多电脑切换器
  18. 市场调研很难做?这些软件帮你理清思绪
  19. 如何快速的下载百度网盘的文件
  20. 支持HEVC/H265 RTMP播放的VLC WINDOWS版本

热门文章

  1. 跳出以人为中心,从事情发展的角度看问题本质
  2. 二元牛顿迭代法matlab,牛顿迭代法解二元方程组以及误差分析 matlab实现
  3. access中所有女生的记录,ACCESS建查询,如何查两个表里的内容
  4. [附源码]计算机毕业设计Node.js志远奶茶网络设计(程序+LW)
  5. html怎么设置过渡字体,web前端入门到实战:css3基础-文本与字体+转换+过渡+动画+案例...
  6. 手工纸盒子_手工折纸迷你抽纸盒步骤详细教程
  7. Android安卓扫名片识别内容技术SDK
  8. 等保建设整改_等保三级必备网络安全设备
  9. ios keychain 不被清理_清理垃圾,恢复出厂设置真的能让手机变快吗?
  10. 计算机网络之HTTP协议