跨域资源共享(CORS)

483 阅读3分钟

CORS是一种允许浏览器与服务器交互时,突破同源策略的机制,该机制得以运行主要依赖HTTP标头(请求头、响应头)。该机制基本已被浏览器及服务端框架实现,一般开发人员无需处理该问题。不过,理解该机制,当接口遇到跨域问题时,有助于指导后端同学解决问题[狗头.jpg]

从请求说起

跨域请求大体分三类,分别如下:

  • 简单请求(Simple requests)

首先 Fetch的规范里没有这个概念,这个概念是 XMLHttpRequest 里的术语。那么什么情况下发起的请求属于简单请求?

  1. 请求方式是 GET、HEAD、POST之一。
  2. 请求头里只包含这些(稍不严谨,了解足够):Accept、Accept-Language、Content-Language、Content-Type、DPR、Downlink、Save-Data、Viewport-Width、Width。
  3. 请求头的 Content-Type 属性值是 application/x-www-form-urlencoded、multipart/form-data、text/plain 之一。
  4. No event listeners are registered on any XMLHttpRequestUpload object used in the request; these are accessed using the XMLHttpRequest.upload property.(MDN web docs Simple requests
  5. 请求中未使用可读流对象。

符合以上五条的请求就算是简单请求。那么简单请求时浏览器和服务器的处理与同源下的请求有什么不同呢?

浏览器的请求头需要添加 Origin(源)信息, 服务器需要在响应头里添加 Access-Control-Allow-Origin,值可以是通配符,也可以是具体的源(<origin> | *)。

处理简单请求就这么简单。和简单请求对应的就是复杂请求(好多文章都这么说,本篇文章不用“复杂请求”这一术语),本篇文章使用“预检请求”(MDN web docs Preflighted requests

  • 预检请求(Preflighted requests)

那不符合简单请求的,基本就属于预检请求。为什么会称之为预检请求呢?因为在这类请求发出之前,需要向服务器发送一次 OPTIONS 请求。这次请求主要告诉服务器三个关键数据

  1. Origin: http://example
  2. Access-Control-Request-Method: <method> -- 将要发出真正请求的请求方式
  3. Access-Control-Request-Headers: <headers> -- 除简单请求里要求以外的请求头 服务器在的响应中会携带四个关键数据
  4. Access-Control-Allow-Origin:<origin> | * -- 允许的源
  5. Access-Control-Allow-Methods:<method> -- 允许的请求方式(可多个)
  6. Access-Control-Allow-Headers: <headers> -- 出简单请求里要求的,允许额外请求头
  7. Access-Control-Max-Age: 86400 -- 单位秒。不在需要发送预检请求的时间。

如果浏览器和服务器达成了一致,那么真正的请求就可以发出了。这次的请求与响应基本同简单请求

当需要发送cookie的时候,又需要携带什么信息呢?

  • 具有凭据的请求(Requests with credentials)

对于 XMLHttpRequest 来说的话,只需在请求实例上设置

var httpInstance = new XMLHttpRequest()
httpInstance.withCredentials = true

对于 Fetch 俩说,只需要在参数加入

 fetch(url, { credentials: "include" })

那么具体的请求会有什么不同吗?没有!响应呢?响应有,多了一个 Access-Control-Allow-Credentials: true

总结

综上,只有需要携带凭据的时候,才需要前端手动处理一下。其余,只要出现跨域问题,直接甩给后端同学就好。如果能够理解CORS,还可以对后端指导一番,就酱!那么如果你们用的浏览器是从坟里挖出来...

参考来源

MDN web docs

Fetch