前言
本片章叙述一下跨域问题,原来业务上也有碰到过此类问题,都是搜索下答案先处理问题,只知如何解决但不知深原。
凡事多问个为什么嘛~这已经是两位前辈对我的深刻教诲了,深记于心。
多问:为什么?
一.跨域问题的由来
二.怎么就算跨域(同源的定义)
三.常见跨域解决方法
四.总结
一.跨域问题的由来
为什么会产生这样一个问题,摆在我们面前呢??
理解跨域,首先必须要了解同源策略。同源策略是浏览器上为安全性考虑实施的非常重要的安全策略。
为了防止某些文档或脚本加载别的域下的未知内容造成泄露隐私,破坏系统等安全行为,
哦~说的很有道理,但脑子中没有什么体会认知吧?那咱们换个角度来说这个问题
假设没有同源, 互联网世界是什么样?
1.链接跳转导致的问题.
http://a.com , 放一个链接到 icbc.com, 然后 window.open来打开,window.open有个返回值句柄,
没有同源,a.com可以拥有对这个页面完全的控制权. 拦截表单,捕获数据,将账号密码上传到a.com等等.
2.ajax请求, 要啥就有啥.
你登录jd.com产生了登陆cookie;
然后打开a.com, a.com通过ajax 请求http://jd.com 的用户信息接口,
这时候因为你登陆jd.com,所以a.com发起访问jd.com自动带上了jd的合法cookie,绕过jd的登陆验证,
然后获取到你京东的订单list ,昵称, 所有私密信息返还给a.com.【是不是有点像CSRF?】
所以,为什么需要同源策略,显而易见,必须得限制跨域
(安全性和方便性是成反比的,同源策略提升了Web前端的安全性,但牺牲了Web拓展上的灵活性。所以,现代浏览器在安全性和可用性之间选择了一个平衡点。在遵循同源策略的基础上,选择性地为同源策略“开放了后门”。例如img script style等标签,都允许垮域引用资源,严格说这都是不符合同源要求的。)
二.怎么就算跨域(同源的定义)
1995年, Netscape 公司在浏览器中引入同源策略/SOP(Same origin policy)
同domain(或ip),同端口,同协议视为同一个域,一个域内的脚本仅仅具有本域内的权限,
可以理解为本域脚本只能读写本域内的资源,而无法访问其它域的资源。这种安全限制称为同源策略。
三.常见跨域解决方法
1.JSONP
在js中,我们直接用XMLHttpRequest请求不同域上的数据时,是不可以的。但是,在页面上引入不同域上的js脚本文件却是可以的,jsonp正是利用这个特性来实现的。
啥意思?正如前边提到的,ajax访问接口时受同源限制的,但是<script src="XXXX">是不受限制的,所以通过此方法避开同源限制。哈哈,本质是因为这样啊,钻了同源的漏网之鱼。【通过script的src来加载,这也解释了为什么jsonp只支持get传输】
为啥还要有callback参数?因为需要一个代理函数做中间人来处理数据,这个参数成了约定的函数名了。
这样jsonp的原理就很清楚了,通过script标签引入一个js文件,这个js文件载入成功后会执行我们在url参数中指定的函数,并且会把我们需要的json数据作为参数传入。所以jsonp是需要服务器端的页面进行相应的配合的。
如果你的页面使用jquery,那么通过它封装的方法就能很方便的来进行jsonp操作了.原理是一样的,只不过我们不需要手动的插入script标签以及定义回掉函数。jquery会自动生成一个全局函数来替换callback=?中的问号,之后获取到数据后又会自动销毁,实际上就是起一个临时代理函数的作用。$.getJSON方法会自动判断是否跨域,不跨域的话,就调用普通的ajax方法;跨域的话,则会以异步加载js文件的形式来调用jsonp的回调函数。
2.CORS
CORS(跨域资源共享,Cross-Origin Resource Sharing)是通过客户端+服务端协作声明的方式来确保请求安全的。
服务端会在HTTP请求头中增加一系列HTTP请求参数(例如Access-Control-Allow-Origin等),来限制哪些域的请求和哪些请求类型可以接受。
可以在代码里写,也可以写在服务器配置文件里。
先解释下有什么配置,不用全写,按需选取配置即可
Access-Control-Allow-Origin:指定授权访问的域
Access-Control-Allow-Methods:授权请求的方法(GET,POST,PUT,DELETE,OPTIONS等)
Access-Control-Allow-Credentials 首部字段用于预检请求的响应,表明服务器是否允许,credentials标志设置为true的请求。
Access-Control-Max-Age:<delta-seconds> 首部字段指明了预检请求的响应的有效时间。delta-seconds 表示该响应在多少秒内有效。
Access-Control-Allow-Headers 首部字段用于预检请求的响应。其指明了实际请求中允许携带的首部字段。
实现ajax跨域访问
配置可以在代码里,
也可以写在服务器配置文件(apache,nginx)里。
1)php代码
header('Access-Control-Allow-Origin:*'); // 指定允许其他域名访问
header('Access-Control-Allow-Methods:POST'); // 响应类型
header('Access-Control-Allow-Credentials:true'); //允许客户端传输cookie
2) Nginx
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Credentials' "true";
3) Apache
Header set Access-Control-Allow-Origin www.a.com
(打开Credentials与Methods方式暂时报错,待尝试)
/**
* yii2 行为方法,自动执行打开跨域@inheritdoc
*/
public function behaviors()
{
return [
'corsFilter' => [
'class' => \yii\filters\Cors::className(),
'cors' => [
'Origin' => ['http://a.com','http://b.com'],//多域名设置
'Access-Control-Allow-Credentials' => true,
]
],
];
}
<!-- 前台跨域代码格式 -->
$.ajax({
type: 'POST',
url: 'http://www.b.com' ,
data: {id:1} ,
dataType: 'json',
xhrFields: {withCredentials: 'true'},
success:function(){alert(121);}
});
效果演示 :以修改apache,nginx配置文件的方法为例子
图1.前台跨域代码
图2.触发跨域后的提示
图3.apache配置打开跨域
图4.nginx打开跨域限制
图5.再次调用跨域成功
c.iframe
本质www.a调用不了www.b下的东西,但是a可以iframe打开一个b域名下的第三个页面c,c与b是同域名让c来调用b
所以ifame的本质就是,把a要调用b的代码逻辑写到c里边,然后把c召唤出来即可,剩下的c来做也不涉及跨域.
这里文章介绍的很好:http://blog.csdn.net/fdipzone/article/details/17619673/
4、其它一些方式
WebSocket、服务器代理、flash socket。这里接不详细叙述说了。
四.总结:
这么一看,跨域限制还是很有必要的,安全。
但是为了方便,同源限制也开放了几个可以跨域访问的标签<img><script><style>等
但是就是因为这个小缺口的开放不也是产生CSRF的可乘之机嘛
所以说,安全与便捷总是相对力点,就像原来听说支付宝想推出一款扫物品就能付款的产品,出门不用带钱包了,带上这个设定的物体就行了,方便是方便了,但是多危险吗丢了别人就随便拿来支付了吗。(不过后来也没见阿里推出该产品哈哈)
五。注意点
1.关于Access-Control-Allow-Origin设置多域名问题
总不能给服务器设置*吧太危险了,但是又想设置多域名,不能简单的加逗号
比如:add_header 'Access-Control-Allow-Origin' 'www.a.com,www.b.com'; 是错误
因该怎么做呢?加入逻辑代码,把它变量化。
$origin = isset($_SERVER['HTTP_ORIGIN'])? $_SERVER['HTTP_ORIGIN'] : '';
$allow_origin = array( 'http://client1.runoob.com', 'http://client2.runoob.com' );
if(in_array($origin, $allow_origin)){header('Access-Control-Allow-Origin:'.$origin); }
if ($http_origin ~ <允许的域(正则匹配)>) { add_header 'Access-Control-Allow-Origin'"$http_origin";
if ($request_method = "OPTIONS") {...}
}
2.关于Credentials设置问题
我们从上文知道,Credentials是开启传输cookie的一个开关
但是注意:
1).首先:服务器Access-Control-Allow-Origin【不能设置成*】 得设置成对应域名,否则无效!!!
2).其次,只在服务端设置开启Access-Control-Allow-Credentials服务器打开不行,
还得客户端打开withCredentials=true,否则,即使服务器同意发送Cookie,浏览器也不会发送。或者,服务器要求设置Cookie,浏览器也不会处理。
3.配置完成以后还是报错, that is not equal to the supplied origin,去看看你allow的域名是否携带了" / ",斜杠,有斜杠会报错
XMLHttpRequest cannot load abc.com/B. The 'Access-Control-Allow-Origin' header has a value 'http://A.abc.org/' that is not equal to the supplied origin. Origin 'http://A.abc.org' is therefore not allowed access.
4.cors预检请求问题
若http请求不是get,post,head,或者携带header头过多,浏览器会先预检请求(option(经测试确实存在两次请求)
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。
参考文章:
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS
iframe与主框架跨域相互访问方法:http://blog.csdn.net/fdipzone/article/details/17619673/
js中几种实用的跨域方法原理详解:http://www.cnblogs.com/2050/p/3191744.html
前端跨域请求如何实现:https://ask.zkbhj.com/?/question/96