0%

译(八)计算机科学可视化——CORS

本文译自技术社区dev.to 原作者 Lydia Hallie
原文链接:https://dev.to/lydiahallie/cs-visualized-cors-5b8h

Introduction

每一个开发人员都会在控制台里碰到这种烦人的问题Access to fetched has been blocked by CORS policy😬 尽管我们有一万种方法可以解决这种错误,但是呢,让我们今天来看一下CORS的机制是怎么运作的

❗️在这篇博客中我将不会讲解HTTP的基础细节

在前端,我们经常有要展示存储在其他地方数据的需求。在我们渲染数据以前,浏览器要先向服务器发送一个HTTP请求去获取数据。

让我们看一下这个过程,我们要在www.mywebsite.com上获取一些用户的信息,这些信息储存在api.website.com

OK了!我们刚刚向服务器发送了一个HTTP请求,然后我们拿到了我们要请求的JSON数据

好了,让我们再试一次,不同的是,我们这次请求的是另外一个域名www.anotherdomain.com

这次我们拿到了CORS错误,让我们来研究一下~

————————

✋🏼 Same-Origin Policy同源策略

web强制实施的一种机制叫做same-origin policy同源策略。在默认情况下,我们只能访问处于same origin同源的资源,例如在刚刚的例子中,我们就可以访问https://mywebsite.com/image1.png

一个跨域资源的定义,是当它处于不同(子)域名(sub)domain不同协议protocal不同端口port

那么,为什么我们要使用同源机制呢?同源机制为什么存在呢?当你不小心点击了你朋友在facebook上发的带有病毒的链接时,这个链接会把你重定向到一个病毒网站,这个病毒网站里嵌入有ifram标签,然后呢,他会通过cookie成功登陆到你的银行账户里。

然后这个病毒网站的开发者可以操作银行网站的DOM内容然后把钱发送到他们的账户里

好吧。。这是一个挺严重的安全问题。但幸运的是,同源策略可以帮助我们解决这个问题。这个策略可以确保我们只能访问同一个源下面的资源

如上图所示,当源www.evilwebsite.com尝试去访问来自www.bank.com的跨域资源时,同源策略阻止了这一行为。

🔥 Client-side CORS 客户端CORS

Emmm…但是我们经常要请求跨域资源,难道我们每次请求跨域资源的时候都要与后端API交互来获取数据吗?为了让跨域请求变得更安全,浏览器有一种机制叫做CORS🥳CORS是Cross-Origin Resource Sharing的缩写。

User agents可以通过在HTTP请求的头部中设定的Origin的值,来实安全的跨域访问✅ 让我们具体来看一下:当一个跨域请求被发出的时候,客户端会自动在HTTP请求中额外添加一个头部叫做Origin。 Origin的值就是我们请求发出的地址

💻 Server-side CORS 服务器端CORS

在后端,我们要允许一个跨域请求的话,必须要HTTP头部中设置Access-Control-*字段。基于这些CORS响应头呢,浏览器现在就就可以访问那些被同源策略阻挡的资源啦

当然,CORS头部可以设置很多,最关键的字段就是Access-Control-Allow-Origin,只有设置了这个,才算是设置了允许跨域访问,这个头部的值代表的含义就是我们要允许哪些地址来请求资源

例如,还是上面的例子,我们现在是一个服务器,然后https://mywebsite.com要来访问我们的资源了,我们就在HTTP response中把https://mywebsite.com加到Access-Control-Allow-Origin头部中

OK,现在这个被添加过得response已经发送回客户端了,此时同源策略就不会限制我们访问在https://api.website.com上的资源了

CORS机制会检查response中Access-Control-Allow-Origin的值是否与request中Origin的值是否匹配(还记得我们一开始发送请求的时候携带了Origin的值了吗)

完美!但是,当我们发送请求一个请求,同时这个请求的origin并没有写在Access-Control-Allow-Origin中的时候,CORS就会报出一个错误。以上这就是我们以前碰到这个错误的具体原因了

当然,如果你愿意的话,你也可以直接使用 * 来允许所有的Origin来请求资源,但是真的有人这么做吗?不会吧不会吧,不会真的有人这么干吧

————————

Access-Control-Allow-Origin只是诸多CORS头部中的一种,服务端也可以扩充CORS来禁止某些特定的请求

另外一个广为人知的就是Access-Control-Allow-Methods,CORS只会允许已经添加的跨域请求方法。如下图,服务端只允许POST、GET、PUT,其他像PATCH和DELETE就会被阻止

咦?说到PUT、PATCH和DELETE,CORS对这几个请求其实有不处理的方法。这些不同寻常的请求被叫做一个preflight request(预检请求)

preflight:reparing for or preliminary to flight (as of an aircraft ) 芜湖~就是起飞前的预检嘛

🚀 Preflighted Requests

CORS有两种不同类型的请求,一个simple request和一个preflighted request。 不管一个请求是简单请求还是一个预检请求,他们取决于请求中所带有的一些值(一句没有用的废话)

简单请求就是当一个请求使用GET或者PUT方法,并且没有其他自定义头部的时候。其他请求,像带有PUTPATCHDELETE的,就是预检请求

如果想要了解一个简单请求需要满足哪些条件的话,请查看MDN

————————

在我们实际的请求发送以前,客户端会生成一个预检请求,这个预检请求会携带一些我们实际要发送请求的信息,然后这些信息会被添加到Access-Control-Request-*头部中🔥

注意!上面我们说的服务端的response中是带的是Access-Control-Allow-Origin,而这里客户端发送的request中带的是Access-Control-Request-*注意区别

这样,这个预检请求就会告诉服务端我们将要发出的请求是什么样的,request的方法是什么啊?还有没有其他头部了啊?可以看下图:

然后服务端接收到预检请求,返回一个空的带有服务端CORS头部信息的HTTP response。浏览器接收到以后,会检查HTTP请求是不是被允许发送✅

好,被允许了!这种情况下,浏览器就会发送真实请求,然后服务端给我们返回要用的数据

如果不是上面的情况的话,CORS就会阻止预检请求的发送,那么真实的请求也就永远不会被发送✋🏼 预检请求可以阻止我们在没有经过CORS允许的情况下就去获取修改服务器的资源这种行为,对于服务器来说,也可以阻止存在安全隐患的跨域请求

💡 为了减少不必要请求的次数,减少服务器的压力。我们可以设置Access-Control-Max-Age头部来缓存我们对于预检请求的相应。浏览器就没必要重新再发一个新的预检请求了

🍪 Credentials

Cookies默认只会在同源请求中发送,当然我们有时候也想把它跨域使用(废话),比如说我们可能想要服务器识别他们的用户这样。

在服务端我们可以在response的CORS头部中添加Access-Control-Allow-Credentials来跨域使用cookies

同时,在客户端中我们request时要携带withCredentials字段并设置为true

OK,我们现在就可以跨域使用cookie了

————————

尽管,CORS给我们带来了许多沮丧的错误。但是他也能让我们愉快的在浏览器里使用跨域请求✨

CORS和同源策略包含的内容还有很多很多,这篇博客仅仅是一个介绍,如果你愿意的话,可以看这篇the W3 spec深入了解💪🏼