1、JSONP
受浏览器同源策略的限制,网页无法向其他域发送ajax请求,但在页面中引入其他域的脚本是可以的,最常见的例子就是在页面中引入cdn服务器上的js文件及图片等静态资源,JSONP正是利用这一特性实现了跨域。
例如,http://test.com/test.html获取http://abc.test.com/data.php上的数据,那么只需要在test.html中加入以下代码即可:
<script>
function handleData(data){
// do something
}
</script>
<script scr="http://abc.test.com/data.php?callback=handleData"></script>
其实第二个标签返回的是一个可执行的js文件,data.php包含这样一段代码:
<?php
$callback = $_GET['callback'];
$data = array(1,2);
echo $callback.'('.json_encode($data)')'
?>
当第二个标签加载完毕后,就会执行handleData([1,2])
2、document.domain
浏览器同源策略中第二个限制是不同域的框架之间不能进行js交互(但可获得彼此的window对象)
例如,在http://abc.test.com/test.html这个页面中有一个iframe起src为http://bcd.test.com/test2.html,以下代码会出现注释中的问题
//test.html
<script>
function ifrLoad(){
var iframe=document.getElementById('iframe');
var ifrWin=iframe.contentWindow; //获得iframe的window对象(但属性和方法几乎不可用)
console.log(ifrWin.document); //无法获得document属性
}
</script>
<iframe src="http://bcd.test.com/test2.html" id="iframe" onload="ifrLoad()" ></iframe>
此时可通过document.domain设置当前文档的原始域实现跨域操作,只要两个页面的原始域相同,就可通过js获得iframe中的各种属性和方法了.
<!-- test.html -->
<script>
document.domain='test.com'
function ifrLoad(){
var iframe=document.getElementById('iframe');
var ifrWin=iframe.contentWindow; //获得iframe的window对象(但属性和方法几乎不可用)
console.log(ifrWin.document); //正常
}
</script>
<iframe src="https:www.baidu.com" id="iframe" onload="ifrLoad()" ></iframe>
<!-- test2.html -->
<script>
document.domain='test.com'
</script>
需要要注意的是,d只能把document.domain设置成自身或更高一级的父域,且主域必须相同。例如:a.b.test.com 中某个文档的document.domain 可以设成a.b.test.com、b.test.com 、test.com中的任意一个,但是不可以设成 c.a.b.test.com,因为这是当前域的子域,也不可以设成example.com,因为主域已经不相同了。
3 、window.name
window.name属性在不同的页面(甚至不同域名)加载后依旧存在(如果没修改则值不会变化),并且可以支持非常长的 name 值(2MB)假设我们在百度首页设置了window.name为'baidu',
我们在同一窗口地址栏中输入谷歌网址,此时我们查看window.name会发现window.name依然为‘baidu’
此时我们修改window.name为'google',再次打开百度,发现window.name变为了‘google’
了解了window.name属性之后,我们想要从test页面中获得test1中的数据就容易多了,可以首先加载test1页面,将test页面需要的数据存在window.name中,然后加载test页面,但貌似这种方式很蠢,解决办法是在test页面中设置一个隐藏的iframe标签用于获取test2页面的window.name,但需要注意的是受同源策略的限制,需要在iframe中的test2加载完毕后,在iframe中加载与test同源的test3页面。
<!-- http://abc.test.com/test.html -->
<script>
function loadData(){
var iframe=document.getElementById('iframe');
iframe.onload=fucntion(){
var data=iframe.contentWindow.name
console.log(data)
}
iframe.src='http://abc.test.com/test3.html'
}
</script>
<iframe src="http://bcd.test.com/test2.html" id="iframe" onload="loadData()" ></iframe>
4、window.postMessage
window.postMessage是HTML5的API,允许跨域在两个窗口/frames之间发送数据。需要注意的是调用postMessage方法的window对象是指接受消息的那个window对象。其调用方式如下:
otherWindow.postMessage(message, targetOrigin, [transfer]);
otherWindow 是其他窗口的一个引用,比如iframe的contentWindow属性、执行window.open返回的窗口对象、或者是命名过或数值索引的window.frames。
argetOrigin 是用来接收消息的那个window对象所在的域,如果不希望做限定,可以用*代替。
transfer 可选,是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。
<!-- http://abc.test.com/test.html -->
<script>
function ifrLoad(){
var iframe=document.geElementById('iframe');
iframe.contentWindow.postMessage('hello world!','*')
}
</script>
<iframe src="http://bcd.test.com/test2.html" id="iframe" onload="ifrLoad()" '></iframe>
<!-- test2.html -->
<script>
window.onmessage=function(e){
console.log(e.data)
}
</script>
5 、跨域资源共享(CORS)
服务器设置Access-Control-Allow-OriginHTTP响应头之后,浏览器将会允许跨域请求,但需要浏览器需要支持HTML5,可以支持POST,PUT等方法
HTML5标准中提出的跨域资源共享(Cross Origin Resource Share,CORS)支持其他的HTTP方法如PUT, POST等,可以从本质上解决跨域问题。
例如,从http://a.com要访问http://b.com的数据,通常情况下Chrome会因跨域请求而报错:
XMLHttpRequest cannot load http://b.com. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://a.com' is therefore not allowed access.
错误原因是被请求资源没有设置Access-Control-Allow-Origin,所以我们在b.com的服务器中设置这个响应头字段即可:
Access-Control-Allow-Origin: * # 允许所有域名访问,或者
Access-Control-Allow-Origin: http://a.com # 只允许所有域名访问
为 xhr设置 withCredentials后CORS方法跨域还可携带Cookie,但 PUT/POST 请求需要注意处理 preflight 请求。
6、Proxy
可以在服务器端设置一个代理,由服务器端向跨域下的网站发出请求,再将请求结果返回给前端,成功避免同源策略的限制。