Ps:浏览器的同源策略:其限制之一就是不能通过ajax的方法请求不同源中的文档。限制二是浏览器中不同域的框架之间不能进行js的交互操作。
一、什么是跨域?
1.JS跨域是指通过js在不同的域之间进行数据传输或通信,如Ajax向一个不同的域请求数据,或者通过JS获取页面中不同域的框架中(iframe)的数据。只要协议、域名、端口有任何一个不同,即为不同的域;
Eg:网址是 http://baidu.com:8080?user=name&pwd=password
http:// 协议
baidu.com 域名(注意:www.baidu.com不是域名)
8080 端口
user=name&pwd=password 地址带的参数
2.在Js中直接使用XMLHttpRequest(Ajax)请求不同域上的数据,是不能实现的;
Eg:相对http://store.company.com/dir/page.html同源检测的结果:
一、通过jsonp跨域
1.在js中,可以在页面上引入不同域上的js脚本文件,jsonp利用这个特性实现跨域。简单来说,请求的文件只要含有“src”,“href”这些属性,就能在A服务器上请求所需的文件,然后在B服务器上运行,实现跨域。
举例:
index.html页面,它里面的代码需要利用ajax获取一个不同域上的json数据,假设json数据地址是http://example.com/data.php则index.html代码为:
<script>
function gaindata(jsondata){
//处理获得的json数据
}
</script>
//jsonp跨域
<script src="http://example.com/data.php?callback=gaindata"></script>
跨域原理:这里的json数据是被当做js文件通过script标签引入的,(http://example.com/data.php返回的必须是一个能执行的js文件) ,这个js文件载入成功后会执行url参数中指定的gaindata函数,并且会将json数据作为参数传入函数中。
PHP代码:
<?php
$callback=$_GET[callback];//获取回调函数callback
$data=array("a","b","c");//设置要返回的数据
echo $callback."(".json_encode($data).")";//输出数据
2. 通过jsonp跨域原理,可以用js动态生成script标签进行跨域操作。若页面使用jquery,那么通过其封装的方法很方便的进行jsonp操作。
<script>
$.getJSON('http://example.com/data.php?callback=?',function(jsondata){
//处理获得的json数据
})
</script>
原理一样,只是jquery会自动生成一个全局函数替换callback=?中的问号,之后获取到的数据又会自动销毁,即是一个临时函数的作用。$getJSON方法会自动判断是否跨域,不跨域,就调用普通的ajax方法,跨域,则会以异步加载js文件的形式来调用jsonp的回调函数。
二、CORS原理跨域
1.JSONP原理,其不足就是只能使用GET提交,若传输的数据量大,这个JSONP方式就使用不了了,相对JSONP,CORS支持POST提交,并且实施起来灰常简单
2.原理:CORS是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。CORS原理只需要向响应头header中注入Access-Control-Allow-Origin:*(*指任意域),页面进行正常的ajax请求即可,这样浏览器检测到header中的Access-Control-Allow-Origin,则就可以跨域操作了。
3.常见后端解决跨域方案:
(1)php后台配置:
第一步:配置Php 后台允许跨域
<?php header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept');
//主要为跨域CORS配置的两大基本信息,Origin和headers
第二步:配置Apache web服务器跨域(httpd.conf中)
原始代码:
<Directory />
AllowOverride none
Require all denied
</Directory>
修改代码:
<Directory />
Options FollowSymLinks
AllowOverride none
Order deny,allow
Allow from all
</Directory>
(2)JAVA后台配置
第一步:获取依赖jar包下载 cors-filter-1.7.jar, java-property-utils-1.9.jar 这两个库文件放到lib目录下。(放到对应项目的webcontent/WEB-INF/lib/下)
第二步:如果项目用了Maven构建的,请添加如下依赖到pom.xml中:
<dependency>
<groupId>com.thetransactioncompany</groupId>
<artifactId>cors-filter</artifactId>
<version>[ version ]</version>
</dependency>
例如:
<script>
/* 2.CORS跨域 允许浏览器向跨源服务器发送XMLHttpRequest请求*/
//2-1 通过配置php后台允许跨域
//header('Access-Control-Allow-Origin:*');
//header('Access-Control-Allow-Headers:Origin,X-Requested-With,Content-Type,Accept')
//主要配置Origin和headers
var ajax=new XMLHttpRequest();
ajax.open("get","http://localhost:80/ajax0609/php/data2.php");
ajax.send();
ajax.onreadystatechange=function (){
if(ajax.readyState==4&&ajax.status==200)
{
console.log(ajax.response);
}
}
</script>
PHP后端配置:
<?php
header('Access-Control-Allow-Origin: *');
$callback=$_GET[callback];
$data=array("a","b","c");
echo $callback."(".json_encode($data).")";
?>
三、通过修改document.domain来跨子域
1.不同的框架之间(父子或同辈),是能够获取到彼此的window对象的,但不能使用获取到的window对象的属性和方法(除h5的postMessage方法)
2.document.domain方法就是将两个界面的document.domain都设置为相同的域名就可以。但是只能设置自身或更高一级的父域,且主域必须相同。且这个方法只适用于不同子域的框架间的交互。
例如:a.b.example.com、b.example.com、example.com中任意一个,但不可以设置成c.a.b.example.com,baidu.com;
a.html界面:
<iframe src="https://www.example.com/b.html" id="iframe" οnlοad="test()"></iframe>
<script>
document.domain='example.com';//设置成主域
function test(){
alert(document.getElementById('iframe').contentWindow);
}
</script>
b.html界面:
<script>
document.domain='example.com';
</script>
四、使用window.name来进行跨域
1. window对象有个name属性,该属性有个特性:即在一个窗口的生命周期内,窗口载入的所有页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。
注意:window.name只能是字符串的形式;最大允许2M左右甚至更大的一个容量,具体取决于不同的浏览器,但一般是够用了;
例如:a.html和b.html同域的数据交互
a.html
<script>
window.name='页面1';//设置window.name的值
setTimeout(function(){
window.location='index.html';
},3000);//3秒更新页面index.html载入到当前的window
</script>
index.html
<script>
alert(window.name);//读取window.name的值
</script>
跨域:a.html和data.html的信息交互 利用iframe获取data.html的数据,然后再去得到iframe获取到的数据;
a.html:
<script>
function getData(){//iframe载入data.html页面后会执行函数
var iframe=document.getElementById("proxy");
iframe.onload=function(){//a.html与iframe已处于同一源,可互相访问
var data=iframe.contentWindow.name;//获取iframe里的window.name,即data.html页面给它设置的数据
alert(data);//成功获取到data.html里的数据
};
iframe.src="b.html";//这是随意的一个页面,只需与a.html同源,能访问到iframe里的东西,设置成about:blank也行
}
</script>
<iframe src="https://www.example.com/data.html" id="proxy" style="display: none" οnlοad="getData()"></iframe>
五、使用H5中新引进的window.postMessage方法来跨域传送数据
1.原理:调用postMessage方法的window对象是指要接收消息的那个window对象,该方法的第一个参数message为要发送的消息,类型只能为字符串;第二个参数targetOrigin用来限定接收消息的那个window对象所在的域,如果不想限定域,可以使用通配符 * 。
需要接收消息的window对象,可是通过监听自身的message事件来获取传过来的消息,消息内容储存在该事件对象的data属性中。
例如:
a.html:
<script>
function onload() {
var iframe=document.getElementById("iframe");
var win=iframe.contentWindow;//获取window对象
win.postMessage("页面a","*");//向不同域的b.html页面发送消息
}
</script>
<iframe src="http://www.example.com/b.html" οnlοad="onload()" id="iframe"></iframe>
b.html:
<script>
window.onmessage=function(){//注册message事件用来接收消息
e=e||event;//获取事件对象
alert(e.data);//通过data属性得到传送的消息
}
</script>