一、什么是跨域?

要理解跨域问题,就先理解好概念。跨域问题是由于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域设为 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>

本地即可获取到数据

第六: 

除了上述常见的五种方法外,

还有flash方式的跨域,可参见

  http://www.cnblogs.com/sevenyuan/archive/2009/11/19/1606237.html

  http://www.2cto.com/Article/201108/100008.html

服务端也可以用一些代理的方式解决,可参见

  http://blog.csdn.net/macky0668/article/details/6247803

等

JS 跨域问题常见的五种解决方式的更多相关文章

  1. js跨域请求数据的3种常用的方法

    由于js同源策略的影响,当在某一域名下请求其他域名,或者同一域名,不同端口下的url时,就会变成不被允许的跨域请求.那这个时候通常怎么解决呢,对此菜鸟光头我稍作了整理:1.JavaScript   在 ...

  2. 谷歌、火狐浏览器下实现JS跨域iframe高度自适应的完美解决方法,跨域调用JS不再是难题!

    谷歌.火狐浏览器下实现JS跨域iframe高度自适应的解决方法 导读:今天开发的时候遇到个iframe自适应高度的问题,相信大家对这个不陌生,但是一般我们都是在同一个项目使用iframe嵌套页面,这个 ...

  3. js跨域原理及解决方案

    方法一:jsonp函数 在HTML DOM中,Script标签是可以跨域访问服务器上的数据的.因此,可以指定script的src属性为跨域的url,基于script标签实现跨域.script标签本身就 ...

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

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

  5. 跨域的小小总结:js跨域及跨域的几种解决方法

    一.什么是跨域?? js跨域请求就是使用js访问iframe里的不同域名下的页面内容,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同的域的iframe框架中的数据.即只要域名.协议. ...

  6. 5种处理js跨域问题方法汇总

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

  7. 【js跨域】js实现跨域访问的几种方式

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

  8. 三种方法实现js跨域访问

    转自:http://narutolby.iteye.com/blog/1464436 javascript跨域访问是web开发者经常遇到的问题,什么是跨域,一个域上加载的脚本获取或操作另一个域上的文档 ...

  9. [转] 三种方法实现js跨域访问

    1.基于iframe实现跨域 基于iframe实现的跨域要求两个域具有aa.xx.com,bb.xx.com这种特点,也就是两个页面必须属于一个基础域(例如都是xxx.com,或是xxx.com.cn ...

随机推荐

  1. Chrome谷歌浏览器首页被改为Hao123导航怎么办|附各类解决方法【转】

    软件小子:昨天偶然间发现自己的chrome浏览器的首页被篡改成hao123导航了,要是自己设置的还无所谓,但是后面还有尾巴.顿时就火了,又是哪款软件这么流氓,太无良了,我非常确定我肯定是没有勾选什么设 ...

  2. DataTable 中varchar 转换为 Double 后重新 排序。

    DataTable  查询出某个字段为varchar 类型的.不过里面存的为数字,需要进行排序.可是如果直接排序就会不对.因为为varchar类型的,需要转换一下. 方法一: dt.Columns.A ...

  3. 每日英语:Success Outside the Dress Code

    Anyone who has felt like the odd duck of the group can take heart from new research from Harvard Bus ...

  4. Windows下安装 msysGit 以及初始化 Git server环境

    Windows下git工具msysGit使用以及Git server初始化 Windows下git工具,这里选择msysGit,版本为msysGit-netinstall-1.8.1.2-previe ...

  5. 【WebMisCentral WMC】基于Extjs 4.2x的企业级用户授权认证中心系统(SSO+AM+SM),多租户SAAS应用

    http://saas.chinacloudtech.com 题记 三年磨一剑,在企业信息化的道路上已经走了3年之久了,3年多时间里做了很多,突破了很多:有无奈和辛酸,也有收货与喜悦:自我价值也在不断 ...

  6. SVN: bdb: BDB1538 Program version 5.3 doesn't match environment version 4.7

    Q:bdb: BDB1538 Program version 5.3 doesn't match environment version 4.7 A: svnadmin recover /var/wh ...

  7. c++中两个类互相引用的问题

    最近在改一个C++程序的时候碰到一条警告信息,警告信息为:“ 删除指向不完整“Q2DTorusNode”类型的指针:没有调用析构函数                1> c:\users\lxw ...

  8. HTML5新特性之Web Worker

    1.概述 JavaScript语言采用的是单线程模型,也就是说,所有任务排成一个队列,一次只能做一件事.随着电脑计算能力的增强,这一点带来很大的不便,无法充分发挥JavaScript的潜能.龙其考虑到 ...

  9. 多条件动态LINQ 组合查询

    本文章转载:http://www.cnblogs.com/wangiqngpei557/archive/2013/02/05/2893096.html 参考:http://dotnet.9sssd.c ...

  10. U3D中IOS平台泛型方法尽少使用

    U3D的IOS最小运行库use micro mscorlib是不包含泛型反射方法的,如FieldType.GetGenericArguments方法.所以尽量少用List而直接使用array. 另外请 ...