CORS跨域资源共享

前言

最近在后期优化梦幻西游手游图库项目的时候,遇到一个H5跨域请求接口的需求。

CORS-Orign Resource Sharing(CORS)既跨域资源共享,众所周知,浏览器有个同源策略,为了处理跨域请求,一般用JSONP,设置documet.domain降域,CORS等方式。

跨域资源共享协议标准通过声明一系列http标准头,让服务器能声明哪些来源可以通过浏览器范围服务器的资源。特别那些会对服务器数据造成影响的方法,标准要求浏览器要先以OPTION方法去发送一个预请求去获知服务器对跨源请求所支持的HTTP方法。以下就会分两种请求进行讨论。

1.简单请求

简单来说,就是一些请求不会触发CORS preflight(预请求的)。例如满足下述条件就是简单请求:

请求是其中这三种方法之一 HEAD,POST,GET

除了用户代理自动设置的头部外,,唯一允许人工设置的头部是是以下这几种情况 Accept,Accept-Language,Content-Language,Content-Type等。

如果使用POST方法,允许Content-Type的值有application/x-www-form-urlencoded,multipart/form-data,text/plain

例如以下这个情景,在测试地址域名下http://test.nie.163.com/请求http://tuku-my.dev.webapp.163.com:8003/的接口,来获取相应的图片列表。

$.ajax({
    url : 'http://tuku-my.dev.webapp.163.com:8003/v1/image',
    data : {
        sort:'new',
        tag:'',
        start:0,
        span:20
    },
    type : 'GET',
    dataType : 'json',
    cache : true,
    xhrFields:{
        withCredentials : true
    },
    success : function(data){
        //do something
    },
    error : function(data){
         
    },
    complete : function(){
 
    }
})

接口请求的是json格式的数据,withCredentials设置为true,从而使得凭证信息(cookie)可以随着请求一起发送。

浏览器发出了哪些请求头

如上图所示,在跨域资源共享时,浏览器可能发出这些请求头:

Origin:http://test.nie.163.com

origin参数是一个URI,告诉服务器端请求来自哪里。

这些请求头信息是在请求服务器端设置的,不需要手动设置。

在这个情景中,服务器返回了什么给浏览器

如上图所示,在跨域资源共享时,服务器需要返回的响应头信息如下:

Access-Control-Allow-Credentials:true

如果服务器的相应中没有这个响应头,则浏览器不会把请求的结果返回给发出请求的脚本程序,以保证信息安全。

Access-Control-Allow-Origin:http://test.nie.163.com
Vary:Origin

orgin参数表示允许向服务器发起请求的URI,如果不带Credentials可以设置为。但是如果指定了域名不是,就要设置vary响应头为origin,它告诉客户端,响应是根据不同请求头来返回内容的。

现在除了http://test.nie.163.com之外,其它站点不能跨域访问http://tuku-my.dev.webapp.163.com:8003/的内容了。

Access-Control-Allow-Methods:GET, POST, PUT, DELETE, OPTIONS

用来标明资源可被请求的方式

Access-Control-Allow-Headers:Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Mx-ReqToken,X-Requested-With

在实际请求中,标明可以使用的自定义的HTTP请求头。

2.非简单请求(有预请求)

如上面所述,不满足简单请求条件的,浏览器就会先发送一个方法为'OPTION'方法请求目的站点。来验证这个跨域请求对于目的站点是不是安全和可接受的。

例如在测试域名下http://test.nie.163.com/请求http://tuku-my.dev.webapp.163.com:8003/的接口,来删除已收藏的某相册。

$.ajax({
    url : 'http://tuku-my.dev.webapp.163.com:8003/v1/user/bookmarks',
    data : {
        album_id:'5860c2a98617ef3a0be83f42'
    },
    type : 'DELETE',
    dataType : 'json',
    cache : true,
    xhrFields:{
        withCredentials : true
    },
    success : function(data){
        //do something
    },
    error : function(data){
         
    },
    complete : function(){
 
    }
})

如图所示,此时浏览器会先发送一个OPTION方法的请求:

查看方法为OPTION请求的头部:

随着OPTION的请求,以下这两个请求头一起被发送:

Access-Control-Request-Headers:
Access-Control-Request-Method:DELETE

服务器返回的相关CORS的响应头有:

Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Mx-ReqToken,X-Requested-With
Access-Control-Allow-Methods:GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Origin:http://test.nie.163.com

对应的头部意思在上面已经叙述过,就不再重复了。

此时Access-Control-Allow-Origin的值是当前请求的域名得知允许访问,以及知道支持的请求方法和头部,然后浏览器进行了第二次的请求。

注意:使用jquery的$.ajax方法在跨域请求下没有发送X-Requested-With请求头,没跨域的时候可以看线上地址是有发送请求头

X-Requested-With:XMLHttpRequest

X-Requested-With这个请求可以判断客户端是Ajax请求还是其它请求,当值为XMLHttpRequest就说明是Ajax请求。

解决方法是可以在header里添加,在真正请求的时候就会发送这个头部。但实际情况是没必要加这个头部的。

headers: {'X-Requested-With': 'XMLHttpRequest'}

3.与JSONP不同的是

JSONP只能支持GET的方式,承载信息量有限,CORS支持所有HTTP请求的类型。

CORS只兼容高级浏览器(浏览器需要支持HTML5),JSONP兼容低版本浏览器。

CORS是W3C标准化组织提出的一种规范机制,允许客户端的跨域请求。而jsonp是在html5之前,处理跨域的一种事实标准,但实际只是hack。


本文由 w3cmark_前端笔记 版权所有,转载时请注明出处。
注明出处格式:w3cmark (http://www.w3cmark.com/2017/556.html)

分享到:

关注w3cmark
微信公众号 w3cmark_com