Q:为什么要跨域?
A:跨域问题是浏览器同源策略(请求的url地址,必须与浏览器上的url地址处于同域上)限制,当前域名的js只能读取同域下的窗口属性;
一个网站的网址组成包括协议名、子域名、主域名、端口号;
当在页面中从一个url请求数据时,若这个url的协议名、域名、端口号任意有一个不同,就会产生跨域问题。
js中几种实用的跨域方法原理详解
这里说的js跨域是指通过js在不同的域之间进行数据传输或通信,如用ajax向一个不同的域请求数据,或通过js获取页面中不同域的框架中(iframe)的数据。
只要协议、域名、端口有任一不同,都被当作是不同的域。
下表给出了相对http://store.company.com/dir/page.html同源检测的结果:
一、通过JSONP跨域
在js中,直接用XMLHttpRequest请求不同域上的数据时,是不可以的。但在页面上引入不同域上的js脚本文件却是可以的,jsonp正是利用这个特性来实现的。
例:有个a.html页面,它里面的代码需要利用ajax获取一个不同域上的json数据,假设这个json数据地址是http://example.com/data.php,那么a.html中的代码就可以这样:
我们看到获取数据的地址后面还有一个callback参数,按惯例是用这个参数名,但是你用其他的也一样。当然如果获取数据的jsonp地址页面不是你自己能控制的,就得按照提供数据的那一方的规定格式来操作了。
因为是当做一个js文件来引入的,所以http://example.com/data.php返回的必须是一个能执行的js文件,所以这个页面的php代码可能是这样的:
最终那个页面输出的结果是:
因此通过http://example.com/data.php?callback=dosomething得到的js文件,就是之前定义的dosomething函数,并且它的参数就是我们需要的json数据,这样就跨域获得了数据。
可用js动态生成script标签来进行跨域操作,而不用手动书写script标签;若页面使用jquery,那么通过它封装的方法就能进行jsonp操作。
二、通过CORS跨域
1…CORS请求原理
CORS是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
核心思想:在服务器端通过检查请求头部的origin,从而决定请求应该成功还是失败
第一种现象:
No 'Access-Control-Allow-Origin' header is present on the requested resource,并且The response had HTTP status code 404
原因如下:
本次ajax请求是“非简单请求”,所以请求前会发送一次预检请求(OPTIONS)
服务器端后台接口没有允许OPTIONS请求,导致无法找到对应接口地址
解决方案:
后端允许options请求
第二种现象:
No 'Access-Control-Allow-Origin' header is present on the requested resource,并且The response had HTTP status code 405
原因如下:
这种现象和第一种有区别,这种情况下,后台方法允许OPTIONS请求,但是一些配置文件中(如安全配置),阻止了OPTIONS请求,才会导致这个现象
解决方案:
后端关闭对应的安全配置
第三种现象:
No 'Access-Control-Allow-Origin' header is present on the requested resource,并且status 200
原因如下:
比如origin头部检查不匹配,比如少了一些头部的支持(如常见的X-Requested-With头部),然后服务端就会将response返回给前端,前端检测到这个后就触发XHR.onerror,导致前端控制台报错
解决方案:
后端增加对应的头部支持
第四种现象:
heade contains multiple values '*,*'
原因如下:
表现现象是,后台响应的http头部信息有两个Access-Control-Allow-Origin:*
解决方案:
建议删除代码中手动添加的*,只用项目配置中的即可;
建议删除IIS下的配置,只用项目配置中的即可
2.常见的后端解决方案:
PHP后台得配置几乎是所有后台中最为简单的,遵循如下步骤即可:
第一步:配置Php 后台允许跨域
第二步:配置Apache web服务器跨域(httpd.conf中)
原始代码:
AllowOverride none
Require all denied
修改代码:
Options FollowSymLinks
AllowOverride none
Order deny,allow
Allow from all
JAVA后台配置只需要遵循如下步骤即可:
第一步:获取依赖jar包下载 cors-filter-1.7.jar, java-property-utils-1.9.jar 这两个库文件放到lib目录下。(放到对应项目的webcontent/WEB-INF/lib/下)
第二步:若项目用了Maven构建的,请添加如下依赖到pom.xml中:
com.thetransactioncompany
cors-filter
[ version ]
三、通过修改document.domain来跨子域
浏览器都有一个同源策略,限制其一:不能通过ajax的方法去请求不同源中的文档; 其二:浏览器中不同域的框架间不能进行js的交互操作。
不同的框架之间(父子或同辈),能够获取到彼此的window对象的,但不能使用获取到的window对象的属性和方法(html5中的postMessage方法是一个例外,还有些浏览器如ie6也可以使用top、parent等少数几个属性)。
例:有一个页面地址是http://www.example.com/a.html , 在此页面中有一个iframe,其src为http://example.com/b.html, 这个页面与它里面的iframe框架是不同域的,所以我们是无法通过在页面中书写js代码来获取iframe中的东西的:
可将http://www.example.com/a.html 和 http://example.com/b.html这两个页面的document.domain都设成相同的域名。需注意,document.domain的设置有限制,只能把document.domain设置成自身或更高一级的父域,且主域必须相同。
比如:a.b.example.com 中某个文档的document.domain 可设成a.b.example.com、b.example.com 、example.com中的任意一个,但不可设成 c.a.b.example.com,因为这是当前域的子域,也不可设成baidu.com,因为主域已经不相同了。
例:在页面 http://www.example.com/a.html 中设置document.domain
在页面 http://example.com/b.html 中也设置document.domain,虽然这个文档的domain就是example.com,但是还是必须显示的设置document.domain的值:
这样就可通过js访问到iframe中的各种属性和对象。
四、使用window.name来进行跨域
1.window.name属性的特征:即在一个窗口(window)的生命周期内,窗口载入的所有页面共享一个window.name,每个页面对其都有读写权限,window.name是持久存在一个窗口载入过的所有页面中,不会因新页面的载入而重置。
例:有一个页面a.html,它里面有这样的代码:
b.html页面的代码:
a.html页面载入后3秒,跳转到了b.html页面,结果为:
在b.html页面上获取到了a.html给window.name设置的值;若在之后所有载入的页面都没对window.name进行修改,则所有这些页面获取到的window.name的值都是a.html页面设置的那个值;其中的任何一个页面都可以对window.name的值进行修改。
注:window.name的值只能是字符串的形式。
以上例中,a.html和b.html处于同一域,即使a.html与b.html处于不同域,上述结论同样适用,这也正是利用window.name进行跨域的原理。
2.如何通过window.name来跨域获取数据?
例:有一www.example.com/a.html页面,需通过a.html里的js来获取另一个位于不同域上的页面www.cnblogs.com/data.html里的数据
data.html即给当前window.name设置一个a.html想得到的数据值。data.html里的代码:
Q:那么在a.html页面中,我们怎么把data.html页面载入进来呢?
A:在a.html中使用一个隐藏的iframe充当中间人,由iframe获取data.html的数据,然后a.html再去得到iframe获取到的数据。
充当中间人的iframe想要获取到data.html里通过window.name设置的数据,只需把此iframe的src设为 www.cnblogs.com/data.html 。即a.html得到iframe所获取到的数据,也就是得到iframe的window.name的值。
注:必须把此iframe的src设成跟a.html同一个域,否则根据同源策略,a.html不能访问到iframe里的window.name属性。
a.html页面的代码:
五、使用HTML5中新引进的window.postMessage方法来跨域传送数据