什么是跨域访问? 因为浏览器同源策略,凡是发送请求url的协议、域名、端口三者之间任意一个与当前页面地址不一样即为跨域。存在跨域的状况:
网络协议不一样,如http协议访问https协议。javascript
端口不一样,如80端口访问8080端口。php
域名不一样,如qianduanblog.com访问baidu.com。前端
子域名不一样,如abc.qianduanblog.com访问def.qianduanblog.com。java
域名和域名对应ip,如www.a.com访问20.205.28.90.nginx
请求的数据是XHR(XMLHttpRequest)浏览器会有跨域的限制。若是不是XHR类型的则不会限制 ajax
咱们的浏览器须要禁止跨域呢?
这里举两个常见的例子:
1.咱们知道一帮浏览器都使用cookie去储存咱们的用户登陆信息。若是容许跨域访问,那么别的网站只须要一段脚本就能够获取你的cookie,从而冒充你的身份去登陆网站,形成很是大的安全问题。
2.假设如今有两个不一样域,若是没有这一安全策略,那么当用户在访问a.com时,a.com的一段脚本就能够在不加载b.com的页面而随意修改或者获取b.com上面的内容。这样将会致使b.com页面的页面发生混乱。apache
跨域的解决办法 其实跨域的解决办法从本质上去区分,能够划分为两类。
第一类:被调用方
第二类:调用方
接下来咱们就开始基于这两种类型去解决跨域问题。json
被调用方的解决办法 这里在说明代码以前,咱们要先知道在咱们的请求中能够分为两种请求:(1)简单请求(2)非简单请求api
简单请求: 在heard里面无定义头 Content-Type为一下几种: text/plain multipart/from-data application/x-www-form-urlencoded 常见非简单请求: put,delete方法的ajax请求 发送json格式的ajax请求 带自定义头的ajax请求
简单请求与非简单请求的区别 简单请求:浏览器会先自行后判断 非简单请求:浏览器先判断后执行(OPTIONS)
所以咱们在访问非简单请求的时候,咱们能够看到发送的方法会是opttions。意思就是服务器先询问服务是否存在相关资源。当存在相关资源的时候才能正常返回。
接下来咱们就能够开展被调用方的解决方案:
解决方案1:JSONP 什么是JSONP?
Jsonp(JSON with Padding) 是 json 的一种"使用模式",可让网页从别的域名(网站)那获取资料,即跨域读取数据。
下面咱们展现JSONP的使用案例跨域
#这里咱们模仿ajax进行一个跨越访问,其datatype须要设置为jsonp #里面的jsonp,设置回调函数的名称为callback <script type="text/javascript"> $.ajax({ url:"http://crossdomain.com/services.php", dataType:'jsonp', data:'', jsonp:'callback', success:function(result) { for(var i in result) { alert(i+":"+result[i]);//循环输出a:1,b:2,etc. } }, timeout:3000 }); </script>
#这是后台返回数据的方法 #能够看出其实就是把返回的数据再包含在与前端的回调函数的名字同样的函数体便可 function api_jsonp_encode($json) { if (!empty($_GET['callbak'])) { return $_GET['callbak'] . '(' . $json . ')'; // jsonp } return $json; // json }
jsonp其实做为一种跨域的解决手段其实存在比较明显的缺陷:
1.服务器须要改动代码 2.只支持get方法 3.发送的不是XHR
解决方法2:增长header头
设置头文件: //设置请求域名 header('Access-Control-Allow-Origin:*'); //设置请求头 header("Access-Control-Allow-Headers:Origin, X-Requested-With, Content-Type, Accept, Authorization"); //设置请求方法 header('Access-Control-Allow-Methods:GET, POST, PUT,DELETE,OPTIONS,PATCH'); header('Access-Control-Allow-Methods:*'); //设置预检命令的缓存头 header('Access-Control-Max-Age:3600'); #这里说明一下,*号表明没有限制全匹配。所以你能够看到们设置的请求域名,还有请求方法都是有*号配置。 #设置御检命令缓存头就是让浏览器当发送一次御检命令后,后期就能够在缓存时间内直接使用缓存,而不须要再次请求。其第二个参数为缓存时间
可是咱们直接上述的配置,只能知足通常状况。由于若是当跨域访问的时候携带cookie,或者自定义头的时候咱们仍是不能成功跨域的所以。针对这两种状况,咱们能够作出如下的调整修改
带Cookie的跨域 //设置容许携带Cookie header("Access-Control-Allow-Credentials: true"); //须要设置与服务器匹配的域名,不能使用* header('Access-Control-Allow-Origin:http://www.xxx.com'); #若是可能须要匹配多个容许域名,能够参考下面的动图作法 //经过系统变量获取origin $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); }
自定义请求头的跨域处理 通常状况下可使用如下的代码设置请求头 header("Access-Control-Allow-Headers:Origin, X-Requested-With, Content-Type, Accept, Authorization"); 可是若是不生效。能够把对应的请求头也写进去例如: header("Access-Control-Allow-Headers:Origin, X-Requested-With, Content-Type, Accept, Authorization,test-header");
以上就是经过被调用方设置header头去解决对应的跨域问题。
可是上述的方法仍是存在必定的局限性。由于加header头本质上仍是会改动了咱们的代码。那么咱们还有没有更好的方式呢?其实咱们能够基于服务器的改造
解决方法3:配置服务器的
ngix的配置 server { listen 80; #监听80端口 server_name demo.com; #本地域名 location / { proxy_pass http://XXXX.com; #把全部请求都转发到此须要提供服务的端口下 add_header Access-Control-Allow-Headers $http_access_control_request_headers'; add_header Access-Control-Allow-Origin $http_origin; add_header Access-Control-Allow-Methods *; add_header Access-Control-Max-Age 3600; add_header Access-Control-Allow-Credentials true; #判断若是进入的是与预检查 if ($request_method = OPTIONS){ return 200; } } }
.apache配置 //修改vhost文件 <virtualhost *:80=""> DocumentRoot "C:/htdocs/demo" #目录路径 ServerName demo.com #域名 ##ErrorLog "logs/dummy-host.localhost-error.log" ##CustomLog "logs/dummy-host.localhost-access.log" common #设置转发(须要开启 proxy_module,proxy_http) ProxyPass/ http://XXXX.com #把请求头的Access-Controller-Request-Headerss值返回到Access-Control-Allow-Headers Header always set Access-Control-Allow-Headers "expr=%{req:Access-Controller-Request-Headers}" #把请求头的orgin值返回到Access-Control-Allow-Orgin Header always set Access-Control-Allow-Orgin "expr=%{req:orgin}" Header always set Access-Control-Allow-Methods "*" Header always set Access-Control-Allow-Credentials "true" Header always set Access-Control-Max-Age "3600" #处理预检命令OPTIONS,直接返回204(开启 headers_module,rewrite_module) RewriteEngine On RewriteCond%{REQUEST_METHOD}OPTIONS RewriteRule^(.*)$"/"[R=204,L] </virtualhost>
以上的服务器配置就是用于跨域请求的被调用方的配置
#咱们刚刚讨论完了被调用方的设置。那么咱们再来讲一下调用方的设置。由于在咱们实际的开发中,有时候被调用方有多是不配合咱们进行跨域访问的修改的,那么咱们只能本身解决跨域问题了。 在通常开发中咱们解决的思路就是使用反向代理。而配置反向代理只能经过咱们的服务器去访问对方的服务器,从而绕过浏览器的这一个槛。
nginx: server { listen 80; #监听80端口 server_name demo.com; #本地域名 #后期全部的调用接口都须要使用该地址 #例如:咱们访问http://aa.com/index/login #改成:/ajaxserver/index/login location/ajaxserver { proxy_pass http://XXXX.com; #须要跨域访问接口的的地址 } }
<virtualhost *:80=""> DocumentRoot "C:/htdocs/demo" #目录路径 ServerName demo.com #域名 ##ErrorLog "logs/dummy-host.localhost-error.log" ##CustomLog "logs/dummy-host.localhost-access.log" common #后期全部的调用接口都须要使用该地址 #例如:咱们访问http://aa.com/index/login #反向代理设置 ProxyPass /ajaxserver http://XXXX.com </virtualhost>
以上就是咱们在跨域工做中赶上的常见状况以及解决思路和事例代码。
转载于猿2048:➩《分享跨域访问的解决方案与基础分析》