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。
手机阅读请扫描下方二维码:
12345678
1
555
555
555
1
1
1
1
1
1
1
1
1
1
1
xiqpwrmtovtudzjdmatp
555
1
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
1
1
555
1
1
1
1
555
1
1
1
1
1
1
1
1
555
1
1
555
1
555
1
1
1
1
1
1
1
1
1
1
1
1
1
555