CORS跨域原理

CORS(Cross-Origin Resource Sharing)

Posted by alovn on April 13, 2022

CORS的全称是Cross-Origin Resource Sharing,在前后端分离的开发模式下,对于跨域一定不会陌生。这是浏览器为了安全而使用的同源策略,也就是说当你的网页的域名和请求api数据的域名不一致的时候就会被浏览器禁止。默认情况下所有违背同源策略的请求都会被浏览器禁止,但是有些资源是不受同源策略限制的,比如说js script文件、css、图片等,这也使得我们的网站页面可以加载cdn的资源。

CORS主要是在服务端进行开发和配置,而对于前端几乎不需要开发就可以支持,因为整个的通信过程都是由浏览器自动完成的。当浏览器发现AJAX是跨域请求的时候,会自动在请求头部添加一些附加信息,有时还会多一个OPTIONS的请求。

CORS请求

CORS的请求分为两类:简单请求和预检请求(非简单请求),浏览器对两种类型的处理是不一样的。

只要同时满足以下两个条件就属于简单请求:

  1. 请求的方式:GET、POST、HEAD 三种之一。
  2. HTTP请求头信息不超出以下几种字段:Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type,同时Content-Type仅限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain。

对于以上条件之外的就是预检请求了:

  1. 请求方式为GET、POST、HEAD之外的类型,比如PUT、DELETE。
  2. 请求头中有自定义的字段。
  3. 比如Content-Type为application/json。

简单请求

1
2
3
4
GET /json HTTP/1.1
Accept: */*
Host: localhost:8080
Origin: http://localhost:3000

如果浏览器发现是CORS的简单请求,会自动添加一个Origin的请求头,用来表示这次请求的来源(协议+域名+端口),服务端再根据接受到的Origin值判断是否同意这个请求。

如果Origin不是可信的源,那么服务端只要正常返回就可以了,浏览器会判断响应头里有没有Access-Control-Allow-Origin字段,如果没有这个字段,浏览器就会抛出一个错误。

如果Origion是可信的,需要在服务端的响应头中添加以下几个字段:

1
2
3
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: X-Request-ID
  1. Access-Control-Allow-Origin 值可以和是请求时的Origin值,也可以是*,表示允许接受任意的源。

  2. Access-Control-Allow-Credentials 是一个bool值,可选,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。如果设为true,则允许Cookie可以包含在请求中一起发给服务器。

  3. Access-Control-Expose-Headers 可选,CORS请求时只能获取到6个字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想获取其它字段,就必须在Access-Control-Expose-Headers里面指定。

需要注意的是,如果发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须明确指定与请求一致的域。Cookie会遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie不会上传。

预检请求

非简单请求的CORS请求称为『预检请求』,浏览器会在正式通信之前会先发生一次HTTP OPTIONS查询请求进行预检(preflight),以获知服务器是否允许该请求、可以使用哪些HTTP动词和头信息字段信息。只有得到肯定答复后,浏览器才会发出正式的XMLHttpRequest请求,否则就会报错。

1
2
3
4
5
6
HTTP/1.1 204 No Content
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Origin,Content-Type,Accept
Access-Control-Allow-Methods: GET,PUT,POST,DELETE
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-MaxAge: 7200
  1. Access-Control-Allow-Methods 必须,逗号分隔的一个字符串,表示服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次”预检”请求。
  2. Access-Control-Allow-Headers 如果浏览器请求包括Access-Control-Request-Headers字段,则该字段必须。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段。
  3. Access-Control-Allow-Credentials 与请求时含义相同
  4. Access-Control-Max-Age 可选,指定本次预检请求的有效期,单位为秒。如上面的7200秒,即允许缓存该条回应2个小时,在此期间,不用再重新发出预检请求。

通过”预检”请求后,浏览器的CORS请求,就和简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。

以上简单介绍了CORS的工作原理。前后端开发人员都应该了解它的实现原理,这样若遇到相关的问题,就可以快速的定位,解决问题就会轻而易举。

参考资料

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS