跨域CORS问题header头 | 青训营笔记

692 阅读4分钟

这是我参与「第四届青训营」笔记创作活动的第4天

前言

众所周知,掘金社区中大部分同学都是处理前端工作的。不知道大家还记不记得第一次和后端对接时的场景?

我是记得的。当我按照后端的要求,把接口全都处理妥当之后——哎?特喵的怎么没有显示呢?赶紧打开开发者工具看一看,这一看不要紧,映入眼帘的全部是大红框框:

image.png

于是我气冲冲的去找了负责后端的同学,迎来的却是一盆冷水——

“我(后端)测试的好好的,怎么到你这儿来就不好用了呢?”

说完还打开Postman当场调给我看,我在自己机器上一试,哎,直接访问是可以的,怎么从浏览器里访问就出不去了呢?

后来才明白了,原来是跨域CORS问题没有处理好!

CORS简介

先来看看MDN上给出的CORS定义:

CORS (Cross-Origin Resource Sharing,跨域资源共享)是一个系统,它由一系列传输的HTTP 头组成,这些 HTTP 头决定浏览器是否阻止前端 JavaScript 代码获取跨域请求的响应。

我们来提取一下关键词:

  • 由一系列HTTP头组成——说明跟被请求的资源返回来的header头有关(好家伙!不是我前端的问题,是后端/运维的问题!)
  • 决定是否阻止...响应——喔,这些header头可以决定我是不是可以访问
  • 获取跨域请求的响应——等等,什么叫跨域请求?MDN上的例子简单粗暴,我们直接搬过来:运行在 https://domain-a.com 的 JavaScript 代码使用 XMLHttpRequest 来发起一个到 https://domain-b.com/data.json 的请求。

到这里是不是豁然开朗了?简单来说,就是后端返回请求中的某些header,可以决定前端是否可以跨域请求相应。

于是接下来的问题是:需要设置哪些header呢?

与CORS相关的header

注:以下定义摘自于MDN

Access-Control-Allow-Origin

Access-Control-Allow-Origin指定了该响应的资源是否被允许与给定的origin共享。

例如:

# 允许所有资源访问
Access-Control-Allow-Origin: * 
# 允许指定资源访问
Access-Control-Allow-Origin: https://abc.com

Access-Control-Allow-Methods

Access-Control-Allow-Methods在对 preflight request.(预检请求)的应答中明确了客户端所要访问的资源允许使用的方法或方法列表。

例如:

# 允许通过POST / GET / OPTIONS方法访问
Access-Control-Allow-Methods: POST, GET, OPTIONS

Access-Control-Allow-Credentials

Access-Control-Allow-Credentials响应头用于在请求要求包含 credentials(Request.credentials 的值为 include)时,告知浏览器是否可以将对请求的响应暴露给前端 JavaScript 代码。

注:所谓的credentials可以是 cookies、authorization headers 或 TLS client certificates。

例如:

# 允许将对请求的响应暴露给前端 JavaScript 代码
Access-Control-Allow-Credentials: true

Access-Control-Allow-Headers

Access-Control-Allow-Headers用于 preflight request(预检请求)中,列出了将会在正式请求的 Access-Control-Request-Headers 字段中出现的首部信息。 一句话:Access-Control-Allow-Headers规定了允许带上的其他header属性。

例如:

Access-Control-Allow-Headers: Authorization, Content-Type, Accept, Origin, User-Agent, DNT, Cache-Control, X-Mx-ReqToken, X-Data-Type, X-Requested-With, X-Data-Type, X-Auth-Token, token

预检请求

在上面的分析中,出现了一个词:预检请求。当我们请求一些复杂资源时,浏览器会自动发出一个“预检请求”,用于检查服务器是否支持 CORS 即跨域资源共享。

以下是来自MDN的一个例子:

一个客户端可能会在实际发送一个 DELETE 请求之前,先向服务器发起一个预检请求,用于询问是否可以向服务器发起一个 DELETE 请求:

OPTIONS /resource/foo
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: origin, x-requested-with
Origin: https://foo.bar.org

如果服务器允许,那么服务器就会响应这个预检请求。并且其响应首部 Access-Control-Allow-Methods 会将 DELETE 包含在其中:

HTTP/1.1 200 OK
Content-Length: 0
Connection: keep-alive
Access-Control-Allow-Origin: https://foo.bar.org
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Access-Control-Max-Age: 86400