一、为何会有跨域问题的存在?javascript
JavaScript出于安全方面的考虑,不容许跨域调用其余页面的对象,即同源政策。php
二、什么是同源?html
1995年,同源政策由 Netscape 公司引入浏览器。目前,全部浏览器都实行这个政策。
最初,它的含义是指,A网页设置的 Cookie,B网页不能打开,除非这两个网页"同源"。所谓"同源"指的是"三个相同"。前端
(1)协议相同html5
(2)域名相同java
(3)端口相同web
具体实例 好比:http://www.example.com/zw/index.html这个网站,它的协议是http://,域名是www.example.com,端口号是80(默认端口能够省略),它的同源状况以下:express
①、http://www.example.com/zwxk/manager.html 同源json
②、https://www.example.com/zw/index.html 不一样源(协议不一样)跨域
③、http://examle.com/zw/index.html 不一样源(域名不一样)
④、http://www.example.com:81zw/index.html 不一样源(端口号不一样)
同源政策的目的
同源策略的目的是为了保证用户信息的安全。防止恶意的网站盗取数据。
设想这样一个情景:A网站是一家银行,用户登陆之后,又去浏览其余的网站B,若是网站B能够读取A网站的Cookie,会发生什么问题?
显然,若是Cookie包含隐私(好比存款总额),这些信息就会泄露,更可怕的是,Cookie每每用来保存用户的登陆状态,若是用户没有退出登陆,其余网站就能够冒用户,随心所欲。由于浏览器同时还规定,提交表单不受同源策略的限制。
因而可知,“同源政策”的必要性,不然Cookie能够共享,互联网就毫无安全可言了。
三、非同源限制范围
随着互联网的发展,“同源政策”愈来愈严格。目前,若是非同源,共有三种行为受到限制。
(1)Cookie、LocalStorage和IndexDB没法获取。
(2)DOM没法得到。
(3)AJAX请求不能发送。
虽然这些限制是必要的,可是有时很不方便,合理的用途也受到影响。下面将介绍如何规避上面三种限制。
4-一、解决跨域一:Cookie如何实现跨域
Cookie是服务器写入浏览器的一段信息,只有同源的网页才能共享,可是,两个网页一级域名相同,只是二级域名不一样,浏览器容许经过设置document.domain共Cookie。
举例来讲,A网站是:http:weibo.qq.com
B网站是:http:lol.qq.com
那么只需设置相同的document.domain,两个网页就可共享Cookie。
如今,A网页经过脚本设置一个Cookie。
B网页就能读到这个Cookie。
注意:这种方法只适用于Cookie和iframe窗口,LocalStorage和IndexDB没法经过这种方法规避,而要使用下文将介绍的PostMessage API。
另外,服务器也能够在设置Cookie的时候,指定Cookie的所属域名为一级域名,好比:.qq.com
这样的话,二级域名和三级域名不用作任何设置,均可以读取这个Cookie。
4-二、解决跨域问题二:如何跨域获取DOM。
若是两个网页不一样源,就没法拿到对方的DOM。典型的例子是iframe窗口和window.open方法打开的窗口,它们与父窗口没法通讯。
好比,父窗口运行下面的命令,若是iframe窗口不是同源将会报错。
上面命令中,父窗口想获取子窗口的DOM,应为跨源致使报错。
反之亦然,子窗口获取主窗口的DOM也会报错。
若是两个窗口一级域名相同,只是二级域名不一样,那么设置4-1介绍的document.domain属性,就可规避同源政策,拿到DOM。
对于彻底不相同的网站,目前有三种方法,能够解决跨域窗口的通讯问题。
(1)片断识别符(fragment identifier)
(2)window.name
(3)跨文档通讯API(Cross-document messaging)
4-2-1:片断识别符
片断识别符指的是,URL的#号后面的部分,好比http://qq.com/x.html#fragment的#fragment。若是只是改变片断标识符,页面将不会从新刷新、
父窗口能够把信息,写入子窗口的片断标识符。
子窗口经过监听hashchange事件获得通知
一样的,子窗口也能够改变父窗口的片断标识符。
4-2-2:window.name:
浏览器窗口有window.name属性。这个属性的最大特色是,不管是否同源,只要在同一个窗口里,前一个网页设置这个属性,后一个网页就能够读取它。
父窗口先发开一个子窗口,载入一个不一样源的网页,该网页将信息写入window.name属性。
接着,子窗口跳回一个与主窗口同域的网址。
而后,主窗口就能够读取子窗口的window.name了。
优势:window.name容量很大,能够防止很是长的字符串;
缺点:必须监听子窗口window.name属性的变化,会影响网页性能。
4-2-3:跨文档消息传输window.postMessage:
上面两种方法都属于破解,HTML5为解决这个问题,引入一个全新的API:跨文档消息传输Cross Document Messaging。
下一代浏览器都将支持这个功能:Chrome 2.0+、Internet Explorer 8.0+, Firefox 3.0+, Opera 9.6+, 和 Safari 4.0+ 。
Facebook已经使用了这个功能,用postMessage支持基于web的实时消息传递。
使用方法:otherWindow.postMessage(message, targetOrigin);
otherWindow: 对接收信息页面的window的引用。能够是页面中iframe的contentWindow属性;window.open的返回值;
经过name或下标从window.frames取到的值。
message: 具体的信息内容,string类型。
targetOrigin: 接受消息的窗口的源(origin),即“协议+域名+端口”。也能够设为“*”,表示不限制域名,向全部窗口发送。
message事件的事件对象event,提供一下三个属性:
(1).event.source:发送消息的窗口
(2).event.origin:消息发向的网站
(3).event.data:消息内容
具体实例:
a.com/index.html中的代码:
b.com/index.html中的代码:
4-三、如何解决跨域LocalStorage。
经过window.postMessage,读写其余窗口的LocalStorage也成为了可能。下面是一个例子,主窗口写入iframe子窗口的LocalStorage。
上面代码中,子窗口将父窗口发送来的消息,写入本身的LocalStorage。
父窗口发送消息的代码以下.
增强版的子窗口接受消息的代码以下。
增强版的父窗口发送消息代码以下:
4-四、如何解决AJAX的跨域:
同源政策规定,AJAX请求只能发给同源的网址,不然就报错。
除了架设服务器代理(浏览器请求同源服务器,再由后者请求外部服务),有三种方法规避这个限制。
(1)JSONP
(2)WebSocket
(3)CORS
4-4-一、JSONP解决AJAX跨域问题:
JSONP是服务器与客户端跨源通讯的经常使用方法。最大特色就是简单适用,老式浏览器所有支持,服务器改造很是小。
它的基本思想是,网页经过添加一个<script>元素,向服务器请求JSON数据,这种作法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。
首先,网页动态插入<script>元素,由它向跨源网址发出请求。
上面代码经过动态添加<script>元素,向服务器example.com发出请求。注意,该请求的查询字符串有一个callback参数,用来指定回调函数的名字,这对于JSONP是必需的。
服务器收到这个请求之后,会将数据放在回调函数的参数位置返回。
因为<script>元素请求的脚本,直接做为代码运行。这时,只要浏览器定义了foo函数,该函数就会当即调用。做为参数的JSON数据被视为javascript对象,而不是字符串,所以避免了使用JSON.parse的步骤。
4-4-二、经过webSocket解决AJAX跨域
WebSocket是一种通讯协议,使用ws://(非加密)和wss://(加密)做为协议前缀。该协议不实行同源政策,只要服务器支持,就能够经过它进行跨源通讯。
下面是一个例子,浏览器发出的WebSocket请求的头信息(摘自维基百科)。
上面代码中,有一个字段是Origin,表示该请求的请求源(origin),即发自哪一个域名。
正是由于有了Origin这个字段,因此WebSocket才没有实行同源政策。由于服务器能够根据这个字段,判断是否许可本次通讯。若是该域名在白名单内,服务器就会作出以下回应。
4-4-三、经过CORS解决AJAX跨域
CORS是跨源资源分享(Cross-Origin Resource Sharing)的缩写。它是W3C标准,是跨源AJAX请求的根本解决方法。相比JSONP只能发GET请求,CORS容许任何类型的请求。
定义:CORS其实出现时间不短了,它在维基百科上的定义是:跨域资源共享(CORS)是一种网络浏览器的技术规范,它为Web服务器定义了一种方式,容许网页从不一样的域访问其资源。而这种访问是被同源策略所禁止的。CORS系统定义了一种浏览器和服务器交互的方式来肯定是否容许跨域请求。 它是一个妥协,有更大的灵活性,但比起简单地容许全部这些的要求来讲更加安全。而W3C的官方文档目前仍是工做草案,可是正在朝着W3C推荐的方向前进。
简言之,CORS就是为了让AJAX能够实现可控的跨域访问而生的。
以往的解决方案:
之前要实现跨域访问,能够经过JSONP、Flash或者服务器中转的方式来实现,可是如今咱们有了CORS。
CORS与JSONP相比,无疑更为先进、方便和可靠。
一、 JSONP只能实现GET请求,而CORS支持全部类型的HTTP请求。
二、 使用CORS,开发者可使用普通的XMLHttpRequest发起请求和得到数据,比起JSONP有更好的错误处理。
三、 JSONP主要被老的浏览器支持,它们每每不支持CORS,而绝大多数现代浏览器都已经支持了CORS(这部分会在后文浏览器支持部分介绍)。
详细内容:
要使用CORS,咱们须要了解前端和服务器端的使用方法。
一、前端
之前咱们使用Ajax,代码相似于以下的方式:
这里的“/relativeHref”是本域的相对路径。
若是咱们要使用CORS,相关Ajax代码可能以下所示:
请注意,代码与以前的区别就在于相对路径换成了其余域的绝对路径,也就是你要跨域访问的接口地址。
咱们还必须提供浏览器回退功能检测和支持,避免浏览器不支持的状况。
如今若是直接使用上面的脚本进行请求,会看到浏览器里控制台的报错以下:
错误显示的很明显,这是由于咱们还未设置Access-Control-Allow-Origin头。
二、服务器
服务器端对于CORS的支持,主要就是经过设置Access-Control-Allow-Origin来进行的。若是浏览器检测到相应的设置,就能够容许Ajax进行跨域的访问。
HTTP 头的设置方法有不少,http://enable-cors.org/这篇文章里对各类服务器和语言的设置都有详细的介绍:
①、Apache:Apache须要使用mod_headers模块来激活HTTP头的设置,它默认是激活的。你只须要在Apache配置文件的<Directory>, <Location>, <Files>或<VirtualHost>的配置里加入如下内容便可:
②、PHP:只须要使用以下的代码设置便可。
以上的配置的含义是容许任何域发起的请求均可以获取当前服务器的数据。固然,这样有很大的危险性,恶意站点可能经过XSS攻击咱们的服务器。因此咱们应该尽可能有针对性的对限制安全的来源,例以下面的设置使得只有http://blog.csdn.NET这个域才能跨域访问服务器的API。
③、Node的配置(基于express):
浏览器支持状况
上图为各浏览器对于CORS的支持状况(绿色为支持,数据来源:http://caniuse.com/cors),看起来至关乐观。主流浏览器都已基本提供对跨域资源共享的支持,因此,CORS才会在国外使用的如此广泛。
将来
从全部的浏览器都支持来看,CORS将成为将来跨域访问的标准解决方案。不管是本身服务器间的跨域访问,仍是开放平台为第三方提供API,都将采用这种统一的解决方案,由于它简单、高效,受到全部主流浏览器的支持。它很是重要,也会让咱们的网络变得更加开放。
本文部份内容转自http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html