在前端开发中跨域取资源是非常常见的,因为浏览器的保护策略,导致不能直接跨域,必须通过一些方法来实现..解决跨域问题的方法非常多,要根据具体需求来选择.
浏览器跨域策略
同一个域名下的 允许通信:http://www.baidu.com/a.js http://www.baidu.com/b.js
同一个域名下的不同文件夹 允许通信:http://www.baidu.com/a.js http://www.baidu.com/src/b.js
同一个域名的不同端口 不允许通信:http://www.baidu.com/a.js http://www.baidu.com:8080/b.js
域名对应IP的 不允许通信:http://www.baidu.com/a.js http://192.168.1.1/a.js
主域名相同 子域不同 不允许通信:http://www.baidu.com/a.js http://script.baidu.com/a.js
不同域名 不允许通信:http://www.baidu.com/a.js http://www.google.com.hk/b.js
如果是因为协议和端口导致的跨域问题,前端是无能为力的
在跨域问题上,域只会通过URL首部来判断,而不会判断两个域是否对应相同的ip上
跨域的几种办法
跨域的解决办法有分为前端办法与后端办法
通过document.domain来跨域
浏览器的同源策略其中之一是不能通过ajax的方法去请求不同源中的文档
另外一个限制是限制浏览器不同域的框架之间的交互
例如有一个页面,地址是http://www.baidu.com/a.html
里面的页面有一个iframe,指向http;//baidu.com/b.html
,显然两个框架是属于不同域的,所以我们无法通过在页面中书写js代码来获取iframe中的内容
a.html url:http://www.baidu.com/a.html
b.html url:http://baidu.com/b.html
|
|
此时,如果我们要突破这个限制,可以将其中一个iframe的domain设置为相同的域,注意document.domain的设置是有限制的,我们只能将document.domain设置为自身或更高一级的父域,并且主域是要相同的
在页面
http://www.baidu.com/a.html
中设置document.domain为baidu.com
,也就是主域1234567<iframe id = "iframe" src="http://baidu.com/b.html" onload = "test()"></iframe><script type="text/javascript">document.domain = 'baidu.com';//设置成主域function test(){alert(document.getElementById('iframe').contentWindow);//contentWindow 可取得子窗口的 window 对象}</script>在页面
http://baidu.com/b.html
中也设置document.domain为主域123<script type="text/javascript">document.domain = 'baidu.com';//在iframe载入这个页面也设置document.domain,使之与主页面的document.domain相同</script>
通过location.hash跨域 –锚点技术
原理:通过改变URL的hash部分来进行双向通信,每个window通过修改其他window的location来发送消息.
需要注意的:IE和Chrome浏览器不允许修改parent.location.hash的值,需要借助父窗口域名下的一个代理iframe
例如,父页面baidu.com/a.html
iframe嵌入的页面是google.com/b.html
要实现两个页面的双向通信可以通过:
a.html传递数据到b.html
- a.html修改iframe的src为
google.com/b.html#paco
- b.html监听到url的变化,触发对应的操作
- a.html修改iframe的src为
b.html传递数据到a.html
- 首先先在b.html下创建一个隐藏的iframe,此iframe时baidu.com域下的,并且挂上需要传递的hash数据
- proxy.html监听到url的变化,修改a.html的url值,因为a.html与proxy.html是同域的,所以可以修改
- a.html监听到url的变化,触发对应的操作
b页面的关键代码
|
|
proxy.html的关键代码
|
|
使用有src的标签
原理:所有具有src的属性的标签都是可以跨域的,包括img``script
限制: 需要创建一个DOM对象,只能用于GET方法
不同的标签发送http请求的时机是不一样的
img
标签在更改src属性的时候就会发送请求,但是script
和iframe
只有在添加到DOM树之后才会发送http请求
|
|
JSONP
原理:<script>
是可以跨域的,并且跨域的脚本中可以直接回调我们定义的url参数中制定的函数
限制:只能用于GET方法
例如,有一个a.html页面,里面的代码需用利用ajax来获取不同域下的json数据,假设这个json数据的地址是http://example.com/data.php
,那么在a页面下的代码可以是:
|
|
对应的后台接受到这个请求后,进行处理,返回数据,后台放回的一定是一个可以执行的js文件
|
|
最后页面输出的是dosomething(['a','b','c'])
JQuery中有帮我们封装了一个JSONP的方法
可以免去我们手动插入script标签和定义回调函数,JQuery会自动生成一个全局函数来替换callback后的问号,之后获取到数据后就会销毁,这个方法会自动判断是否跨域,如果不跨域的话,就改用普通的ajax方法,如果跨域,就调用jsonp的回调函数
|
|
navigation对象
原理:iframe之间共享navigation
对象,可以用他来传递数据
限制:只用用在IE6/7上
不做过多介绍
CORS 跨域资源共享
原理:服务端通过设置Access-Control-Allow-Origin
的HTTP响应头后,浏览器会允许跨域
限制:浏览器需要支持HTML5
因此,实现CORS通信的关键在于服务器,只要服务器实现的CORS的接口,就可以跨源通信
例如http://a.com
要访问http://b.com
的数据,这个时候通常会报错,只需要在被访问的http://b.com
的服务器中设置Access-Control-Allow-Origin
即可
允许跨域访问
|
|
使用window.postMessage
原理: HTML5允许窗口之间发送消息
限制:需要HTML5的支持,并且获取窗口句柄后才能互相通信
|
|
|
|
总结
在HTML5之前,JSONP是一个跨域的事实标准,jQuery都给予支持,但是HTML5出现后,最好还是用CORS头和window.postMessage来,从机制上解决跨域的问题。值得注意的是Access-Control-Allow-Origin
头字段是资源所在的服务器上设置的,访问控制的责任仍然是资源提供方,这是和JSONP一致的