跨域访问

1.什么是跨域访问
JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象。
首先什么是跨域,简单地理解就是因为JavaScript同源策略的限制,a.com 域名下的js无法操作b.com或是c.a.com域名下的对象。更详细的说明可以看下表:
URL                             说明            是否允许通信
http://www.a.com/a.js
http://www.a.com/b.js         同一域名下            允许
http://www.a.com/lab/a.js
http://www.a.com/script/b.js  同一域名下不同文件夹    允许
http://www.a.com:8000/a.js
http://www.a.com/b.js         同一域名,不同端口     不允许
http://www.a.com/a.js
https://www.a.com/b.js        同一域名,不同协议     不允许
http://www.a.com/a.js
http://70.32.92.74/b.js       域名和域名对应ip      不允许
http://www.a.com/a.js
http://script.a.com/b.js      主域相同,子域不同     不允许
http://www.a.com/a.js
http://a.com/b.js             同一域名,不同二级域名(同上)    不允许(cookie这种情况下也不允许访问)
http://www.cnblogs.com/a.js
http://www.a.com/b.js         不同域名             不允许

跨域的解决方案:
   1.jsonp比较常用
   2.CORS(Cross-Origin Resource Sharing)
JOSNP解决方案:
就是利用<script>标签没有跨域限制来达到与第三方通讯的目的。当需要通讯时,本站脚本创建一个<script>元素,地址指向第三方的API网址,形如:
<script src=”http://www.example.net/api?param1=1&param2=2″></script>
并提供一个回调函数来接收数据(函数名可约定,或通过地址参数传递)。
第三方产生的响应为json数据的包装(故称之为jsonp,即json padding),形如:
callback({“name”:”hax”,”gender”:”Male”})
这样浏览器会调用callback函数,并传递解析后json对象作为参数。本站脚本可在callback函数里处理所传入的数据。
跨域访问:
地址:http://p.3.cn/prices/mgets?skuIds=J_2146845&type=1&callback=setPrice
回调:setPrice
数据格式:[{"id":"J_2146845","p":"139.00","m":"159.00"}]
html代码:
<button id="btn">按钮</button>
<div id="div"></div>
js代码:
var div = document.
        getElementById("div");
var oScript = null;
var btn = document.getElementById("btn").onclick = function () {
    //伪造一个script加载过程
    oScript = document.createElement("script");
    oScript.src = "http://p.3.cn/prices/mgets?skuIds=J_2325379&type=1&callback=setPrice";
    document.body.appendChild(oScript);
};
function setPrice(str){
    div.innerHTML = str[0].p;
    console.log(str[0].m);
    document.body.removeChild(oScript);
}

cors解决方案:

这是一个W3C标准(显然比jsonp背景深厚许多),同样需要浏览器和服务器同时支持,但是整个通信过程,都是浏览器自动完成,不需要用户参与,就像平时写Ajax一样(如果使用的是jquery的话)。
下面是原生js实现CORS的代码

function createCORSRequest(method,url){
        var xhr=new XMLHttpRequest();
        if("withCredentials" in xhr){
            xhr.open(method,url,true);
        }else if(typeof XDomainRequest != "undefined"){//IE10之前的版本使用XDmainRequest支持CORS
            xhr=new XDomainRequest();
            xhr.open(method,url);
        }else{
            xhr=null;
        }
        return xhr;
    }
    var request=createCORSRequest("get","待访问的地址");
    if(request){
        request.onload=function(data){
            //do sth
        };
        request.send();
    }

适用场景:

承载的信息量大,get形式搞不定,需选用post传输。CORS支持所有类型的传输。

兼容性:

移动端全面支持(除opera mini),PC上IE8+。

CORS思想:

使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。(请求和响应都不包含cookie)
当然如果设置成下面这样,所有的跨域都可以实现了,但这样毕竟太不安全。
“Access-Control-Allow-Origin:*”;//允许任何域向我们的服务器发送请求

一般情况下,浏览器发送一个额外的Origin头部(由浏览器自动生成发送)
Origin:http://localhost:8080//本地网址

然后由服务器发送一个响应表头:Access-Control-Allow-Origin,如果服务器接收该请求,返回值(只能是通配符或单域名。)就和请求值一样。

Access-Control-Allow-Origin:http://localhost:8080

=>CORS方案的重点其实就在于服务器端的配置。

简单请求

  • 只使用 GET, HEAD 或者 POST 请求方法。如果使用 POST 向服务器端传送数据,则数据类型(Content-Type)只能是 application/x-www-form-urlencoded, multipart/form-data 或 text/plain中的一种。
  • 不会使用自定义请求头(类似于 X-Modified 这种)。HTTP头部信息不超出以下{Accept,Accept-Language,Content-Language,Last-Event-ID,content-type(只限于上面提到的3种类型)}

对于简单请求,浏览器直接发出CORS请求。浏览器会自动在头信息(Request Headers)中,添加一个Origin 字段,来表明本次请求来自哪个域。

Paste_Image.png

如果这个源不在许可范围内,会报错: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
如果Origin指定的域名在许可范围内(必须是跨域了的),Response Headers中会多出几个头信息字段。

Access-Control-Allow-Credentials:true//值为true表示允许发送cookie
Access-Control-Allow-Methods:GET, POST, OPTIONS
Access-Control-Allow-Origin:http://localhost:8080
Access-Control-Max-Age:1728000
withCredentials属性

因为CORS默认不发送cookie和http认证,如果要把Cookie发到服务器,就要指定Access-Control-Allow-Credentials:true;
另外AJAX中也要打开withCredentials属性。

var xhr=new XMLHttpRequest();
xhr.withCredentials=true;

jquery ajax请求参数中加入

xhrFields: {
  withCredentials: true
}

非简单请求

除了上面说的简单请求外都是非简单请求,比如:请求方法是PUT
或DELETE,或者Content-Type字段的类型是application/json,又或者有自定义请求头Access-Control-Request-Headers: X-Custom-Header。
比如,我添加自定义请求头

xhr.setRequestHeader('Some-Custom-Response-Header', 'value');

就会发现连续向同一地址请求了两次,而第一次请求什么值也没拿到

Paste_Image.png

第二次请求

Paste_Image.png

这是因为浏览器发现,这是一个非简单请求,就自动发出一个”预检”请求,要求服务器确认可以这样请求。”预检”请求用的请求方法是OPTIONS,表示这个请求是用来询问的。”预检”请求之后,浏览器球会进行正常CORS请求。

服务器端配置(rails为例)

1、可以参考这个方法:为 RESTful API 配置 CORS 实现跨域请求,写的挺详细的。然而对于rails并不算熟悉的我来说,有gem包,怎么可能放着不用呢~
2、这是Rack CORS 中间件的项目地址,按照文档的说明1分钟就可以基本搞


 


	

1 Reply to “跨域访问”

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注