跨域资源共享CORS

我想,最好的感情是两个人都用力的活,一起体验人生的种种趣味,也能包容与鼓励对方。当对方为你打开新的世界,你就没有因为喜欢一个人而拒绝了整个世界。

CORS是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing)。
它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

两种请求

浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
只要同时满足以下两大条件,就属于简单请求。
1.请求方法是以下三种方法之一:

  • HEAD
  • GET
  • POST

2.HTTP的头信息不超出以下几种字段:

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
    Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
    凡是不同时满足上面两个条件,就属于非简单请求。
    浏览器对这两种请求的处理,是不一样的。

预检请求

非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。
非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为”预检”请求(preflight)。
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。

“预检”请求用的请求方法是OPTIONS,表示这个请求是用来询问的。头信息里面,关键字段是Origin,表示请求来自哪个源。
为防止浏览器预检发出 options 请求 时出现 404 错误, 我们可以直接将 options请求结束掉;
以下是ThinkPHP 5利用行为特性解决跨域问题;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php

namespace app\api\behavior;

class CORS
{
public function appInit(&$params)
{
header('Access-Control-Allow-Origin: *');
header("Access-Control-Allow-Headers: token,Origin, X-Requested-With, Content-Type, Accept");
header('Access-Control-Allow-Methods: POST,GET');
if(request()->isOptions()){
exit();
}
}
}

一个例子

jquery 请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
$.ajax({
type:"POST",
url:"http://www.shop.dev/onValidateEmail",
data:{email:"yangguoqi@olmailorg"},
datatype: "json",
success:function(data){
console.log(data);
},
error: function(msg){
console.log(msg);
}
});
</script>

服务端响应

1
2
3
4
5
6
7
8
9
10
11
12
13
Route::post('/onValidateEmail', function() {
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: *");
header("Content-Type: application/json");
$email = post('email','');
$count = \October\Rain\Auth\Models\User::where('email',$email)->count();
return Response::json([
'status' => 200,
'data' => $count
]);

});

与JSONP的比较

CORS与JSONP的使用目的相同,但是比JSONP更强大。
JSONP只支持GET请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。