对CORS跨域的一些理解

799 阅读4分钟

之前面试有被问到: CORS是怎么解决跨域问题的, 之前一直只知道个大概,没有好好的复盘一下。趁着今天睡不着,写一篇文章记录一下自己对于CORS解决跨域问题的一些理解

那什么是跨域?

当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域

跨域是用来限制什么的?

简单来说, 跨域的概念来自于浏览器的同源策略。 目的就是为了保护网站的安全,防止用户信息泄露,防止身份伪造等。 这个又是另外一个知识点了,就不展开讲了

开始正题

我们经常在调试接口的时候, 经常看到类似于这样的情况

options.png

明明我只发送了一个请求, 为什么浏览器却发送了 两次请求, 究其原因其实就是发送的这个请求命中了CORS 复杂请求类型。 浏览器首先需要发送一个options请求跟服务器确认本次请求携带的信息是否在后端约束范围

上面提到了几个关键:两次请求、 复杂请求、 options、 约束范围

一个一个来解读

两次请求

为什么发送两次请求?

当浏览器跨域访问另一个域名接口时, 会触发浏览器的同源策略, 如果后端采用的是CORS解决跨域问题时, 浏览器会先发送一个预检请求(options) 来跟后端端口进行校验,校验通过才会发送下一次请求。这就是为什么会出现两次请求的原因。

复杂请求简单请求

首先明确一点,发送预检请求的前提是要命中 CORS 的复杂请求情况,那什么是 简单请求 和 复杂请求?

简单请求
  • 使用下列方法之一

    • GET
    • HEAD
    • POST
  • 除了被用户代理自动设置的首部字段(例如 Connection ,User-Agent)和在 Fetch 规范中定义为 禁用首部名称 的其他首部,允许人为设置的字段为 Fetch 规范定义的 对 CORS 安全的首部字段集合。该集合为:

    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type (需要注意额外的限制)
    • DPR
    • Downlink
    • Save-Data
    • Viewport-Width
    • Width
  • Content-Type 的值仅限于下列三者之一:

    • text/plain
    • multipart/form-data
    • application/x-www-form-urlencoded

满足以上所有条件才视为简单请求

options

options请求的作用是用于试探性的服务器响应是否正确,即是否能接受真正的请求,如果在options请求之后获取到的响应是拒绝性质的,例如500等http状态,那么它就会停止第二次的真正请求的访问 options请求不会发送类似于token这种开发者自定义的头部字段

约束范围

要满足后端定义的请求头才能通过options请求

  • Access-Control-Allow-Origin: 哪些域名可以访问
  • Access-Control-Allow-Headers: 允许携带哪些请求头
  • Access-Control-Allow-Methods: 允许通过哪些方法访问
  • Access-Control-Max-Age: 可以看成是当前请求缓存时间,单位为秒,设置为 -1 表示复杂请求必须进行options请求。Access-Control-Max-Age只针对当前请求

理论都讲完了, 接下来实操一下

首先操作一下后端什么都不设置的情况

cors1.gif

这里我什么后端什么都没有设置 直接跨域了

看一下报错

cors1.png

提示 No Access_Control-Allow-Origin

接下来修改一下后端代码

// koa2代码, 不用管
ctx.set('Access-Control-Allow-Origin', '*')

cors2.gif

看一下报错

cors2.png

没有报 No Access_Control-Allow-Origin 错误了 但是报 not allowed by Access-Control-Allow-Headers 错误

接下俩我们把 Access-Control-Allow-Headers 也放开


ctx.set('Access-Control-Allow-Headers', '*')

cors3.gif

可以看到两次接口都请求成功了

接下来我们来看设置 Access-Control-Max-Age 的情况

// 设置options缓存3秒的情况
ctx.set('Access-Control-Max-Age', '3')

cors4.gif

可以看到, 在第一次请求的时候发了两次请求, 5秒内再次请求只发送了一次请求, 5秒之后又发送了两次请求

以上就是这次的实操了。

上面的例子 Access-Control-Allow-Headers Access_Control-Allow-Origin 都是设置的 *, 线上肯定是不能这样用的。

还有 Access_Control-Allow-Methods, 大家可以自己去试一下

关于 Access-Control-Allow-Credentials 的情况说明:

如果设置了 Access-Control-Allow-Credentials: true

ccess_Control-Allow-Origin Access-Control-Allow-Headers Access_Control-Allow-Methods 就不能设置为 * 了, 否则还是会报跨域错误

大家可以自己去实操一下

关于CORS的东西我的理解暂时就这么多了, 有新的理解再继续码

参考 86driver's blog