JavaScript跨域问题
跨域产生的原因
JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象,其实就是因为JavaScript同源策略的限制,只有同一域名下,或者同一域名的不同文件夹之间允许通信.
当两个域具有相同的协议, 相同的端口,相同的host,就可以认为它们是相同的域
同源策略
同源策略是一个很重要的安全理念,它在保证数据的安全性方面有着重要的意义。同源策略规定跨域之间的脚本是隔离的,一个域的脚本不能访问和操作另外一个域的绝大部分属性和方法
同源策略虽然安全但却影响了跨域资源共享,所以在跨域请求上就产生了很多宝贵经验.
跨域资源共享(CORS)
定义了必须在访问跨域资源时,浏览器与服务器应该如何沟通。CORS的基本思想就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败
浏览器将CORS请求分成两类:简单请求(simple request
)和非简单请求(not-so-simple request
)
服务器端对于CORS的支持,主要就是通过设置Access-Control-Allow-Origin来进行的。如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问
目前的跨域方法
跨域资源共享分为单向跨域和双向跨域
一、通过JSONP(JSON with Padding)跨域
在js中,我们直接用XMLHttpRequest请求不同域上的数据时,是不允许的。但是,在页面上引入不同域上的js脚本文件却是可以的,我们可以通过script标记来动态加载其他域的资源,JSONP就是利用这个特性来实现的.
JSONP由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数,而数据就是传入回调函数中的JSON数据
优点:
- JSONP属于单向跨域
- 简单高效,易于实现
- 兼容性好,大部分浏览器都可运行
缺点:
- 执行第三方的脚本存在安全隐患,适合受信任的双方使用
- 支持GET请求而不支持POST等其它类型的HTTP请求
- 只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题
CORS和JSONP对比
- JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求
- 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理
- JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS
二、使用window.name来进行跨域
window对象有个name属性,该属性在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,不会因新页面的载入而进行重置
我们可以在页面A中用iframe加载其他域的页面B,而页面B中用JavaScript把需要传递的数据赋值给window.name,iframe加载完成之后,页面A修改iframe的地址,将其变成同域的一个地址,然后就可以读出window.name的值了.
优点:
这个方式非常适合单向的数据请求,而且协议简单、安全,不会像JSONP那样不限制地执行
三、通过修改document.domain来跨子域
通过修改document的domain属性,我们可以在域和子域或者不同的子域之间通信。同域策略认为域和子域隶属于不同的域,比如www.a.com和sub.a.com是不同的域,这时,我们无法在www.a.com下的页面中调用sub.a.com中定义的JavaScript方法。但是当我们把它们document的domain属性都修改为a.com,浏览器就会认为它们处于同一个域下,那么我们就可以互相调用对方的method来通信了
不过如果你想在http://www.example.com/a.html页面中通过ajax直接请求http://example.com/b.html页面,即使你设置了相同的document.domain也还是不行的,所以修改document.domain的方法只适用于不同子域的框架间的交互.
(修改document.domain属于双向跨域)
四、使用HTML5中新引进的window.postMessage方法来跨域传送数据
window.postMessage(message,targetOrigin)
方法是html5新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源,目前IE8+、FireFox、Chrome,Opera等浏览器都已经支持该方法
调用postMessage方法的window对象是指要接收消息的那一个window对象
-
该方法的第一个参数message为要发送的消息,类型只能为字符串;
-
第二个参数targetOrigin用来限定接收消息的那个window对象所在的域,如果不想限定域,可以使用通配符 *
需要接收消息的window对象,可以通过监听自身的message事件来获取传过来的消息,消息内容储存在该事件对象的data属性中。上面所说的向其他window对象发送消息,其实就是指一个页面有几个框架的情况,因为每一个框架都有一个window对象。不同域的框架间是可以获取到对方的window对象的,也可以使用window.postMessage这个方法。
(window.postMessage属于双向跨域)
此外的跨域方式还有
-
flash (单向,双向)
借助flash发送http请求
Flash LocalConnection(双向) -
server proxy(单向)等