✋🏼🗺 🔥 CS 可视化:CORS
✋🏼🔥 CS 可视化:CORS
每一位开发人员都会时不时地在控制台中看到那个大红色的“访问 fetched 已被 CORS 策略阻止”错误,这让他们很沮丧!😬 虽然有一些方法可以快速摆脱这个错误,但今天我们不要想当然!相反,让我们看看 CORS 实际上在做什么,以及为什么它实际上是我们的朋友 👏🏼
❗️ 在这篇博文中,我不会解释 HTTP 基础知识。如果你想了解有关 HTTP 请求和响应的更多信息,我之前写了一篇关于它的小博文 🙂 在我的示例中,我使用 HTTP/1.1 而不是 HTTP/2,这不会影响 CORS。
| 快捷方式 |
| ✋🏼 同源策略 |
| --- |
| 🔥 客户端 CORS |
| --- |
| 💻 服务器端 CORS |
| --- |
| 🚀 飞行前请求 |
| --- |
| 🍪 凭证 |
| --- |
在前端,我们经常想要显示位于其他地方的数据!在显示这些数据之前,浏览器首先必须向服务器发出请求以获取该数据!客户端发送一个 HTTP 请求,其中包含服务器需要的所有信息,以便将数据发送回客户端 🙂
假设我们正尝试从位于“api.website.com”的服务器获取我们“www.mywebsite.com”网站上的一些用户信息!
完美!😃 我们只需向服务器发送一个 HTTP 请求,服务器就会返回我们请求的 JSON 数据。
让我们尝试从**另一个域**发出请求。我们现在不再从“www.mywebsite.com”发出请求,而是从位于“www.anotherdomain.com”的网站发出请求。
等等,什么?我们发送了完全相同的请求,但这次浏览器却显示了一个奇怪的错误?
我们刚刚看到了 CORS 的实际作用!💪🏼让我们看看为什么会发生此错误,以及它到底意味着什么。
✋🏼 同源策略
网络强制执行所谓的 **同源策略**。默认情况下,我们只能访问与请求来源 **同源** 的资源!💪🏼 例如,加载位于 `https://mywebsite.com/image1.png` 的图像是完全可以的。
当资源位于不同的(子)域、协议或端口时,它就是跨源的!

很酷,但是为什么同源策略会存在呢?
假设同源策略不存在,而你不小心点击了你姑姑在 Facebook 上发给你的众多病毒链接之一。此链接会将你重定向到一个“邪恶网站”,该网站嵌入了一个 iframe,可加载你的银行网站,并通过一些设置的 cookie 成功让你登录!😬
这个“邪恶网站”的开发者让网站能够访问这个 iframe 并与您银行网站的 DOM 内容进行交互,以便代表您将钱汇到他们的账户!
是的……这是一个巨大的安全风险!我们不希望任何人都能访问所有内容 😧
幸运的是,同源策略在这里帮了我们大忙!此策略确保我们只能访问来自**同源**的资源。
在这种情况下,源“www.evilwebsite.com”试图从“www.bank.com”访问跨源资源!同源策略阻止了这种情况的发生,并确保了邪恶网站的开发人员无法访问我们的银行数据🥳
好的,那么...这与 CORS 有什么关系?
🔥 客户端 CORS
虽然同源策略实际上只适用于脚本,但是浏览器将此策略“扩展”到了 JavaScript 请求:默认情况下,我们只能访问从**同源**获取的资源!
嗯,但是……我们经常需要访问跨域资源🤔也许我们的前端需要与后端 API 交互才能加载数据?为了安全地允许跨域请求,浏览器使用了一种称为 **CORS** 的机制!🥳
CORS 代表 **跨域资源共享**。虽然浏览器不允许我们访问不在同一来源的资源,但我们可以使用 CORS 稍微改变这些安全限制,同时仍然确保我们安全地访问这些资源 🎉
用户代理(例如浏览器)可以使用 CORS 机制来**允许跨域请求**,否则这些请求将被阻止,这取决于 HTTP 响应中某些 CORS 特定标头的值!✅
当发出跨域请求时,客户端会自动在我们的 HTTP 请求中添加一个额外的标头:“Origin”。Origin 标头的值就是请求的来源!
为了让浏览器允许访问跨源资源,它期望服务器响应中的某些标头,这些标头指定该服务器是否允许跨源请求!
💻 服务器端 CORS
作为服务器开发人员,我们可以通过在 HTTP 响应中添加额外的标头来确保允许跨源请求,这些标头均以 `Access-Control-*` 开头🔥 根据这些 CORS 响应标头的值,浏览器现在可以允许某些通常会被同源策略阻止的跨源响应!
尽管我们可以使用多个 CORS 标头,但浏览器需要一个标头才能允许跨域资源访问:`Access-Control-Allow-Origin`!🙂
此标头的值指定**哪些来源被允许访问他们从服务器请求的资源**。
如果我们正在开发 `https://mywebsite.com` 应该有权访问的服务器,我们可以将该域的值添加到 `Access-Control-Allow-Origin` 标头中!
太棒了!🎉 此标头现在已添加到服务器发送回客户端的响应中。通过添加此标头,如果我们从“https://mywebsite.com”发送请求,**same-policy origin 将不再限制我们接收位于“https://api.mywebsite.com”来源的资源**!
浏览器中的 CORS 机制会检查 `Access-Control-Allow-Origin` 标头的值是否等于请求发送的 `Origin` 的值 🤚🏼
在这种情况下,我们的请求的来源是“https://www.mywebsite.com”,它在“Access-Control-Allow-Origin”响应标头中列出!
完美!🎉 我们能够成功接收跨源资源!那么当我们尝试从“Access-Control-Allow-Origin”标头中未列出的来源访问这些资源时会发生什么?🤔
啊,是的,CORS 会抛出臭名昭著的错误,有时会让人很沮丧!但现在我们实际上看到它完全有道理
The 'Access-Control-Allow-Origin' header has a value 'https://www.mywebsite.com' that is not equal to the supplied origin.
在这种情况下,提供的来源是“https://www.anotherwebsite.com”。但是,服务器在“Access-Control-Allow-Origin”标头中的允许来源列表中没有此提供的来源!CORS 成功阻止了请求,我们无法在代码中访问获取的数据😃
CORS 还允许我们添加通配符 `*` 作为允许来源的值。这意味着来自 的请求应该可以访问所请求的资源,所以要小心!
`Access-Control-Allow-Origin` 是我们可以提供的众多 CORS 标头之一。服务器开发人员可以扩展服务器的 CORS 策略,以便(禁止)允许某些请求!💪🏼
另一个常见的标头是 `Access-Control-Allow-Methods` 标头!CORS 仅允许使用列出的方法发送的跨域请求。
在这种情况下,只允许使用 `GET`、`POST` 或 `PUT` 方法的请求!其他方法(例如 `PATCH` 或 `DELETE`)将被阻止❌
如果您对其他可能的 CORS 标头是什么以及它们的用途感到好奇,请查看此列表。
说到 `PUT`、`PATCH` 和 `DELETE` 请求,CORS 实际上以不同的方式处理这些请求!🙃 这些“”请求会启动所谓的**预检请求**!
🚀 预检请求
CORS 有两种类型的请求:**简单请求**和**预检请求**。请求是简单请求还是预检请求取决于请求中的一些值(别担心,你不必记住这些,哈哈)。
如果请求是 `GET` 或 `POST` 方法且没有任何自定义标头,则请求很简单!任何其他请求(例如带有 `PUT`、`PATCH` 或 `DELETE` 方法的请求)都将被预检。
如果你只是好奇一个请求必须满足哪些要求才能成为一个简单的请求,MDN 有一个有用的列表!
好的,但是“预检请求”到底是什么意思,为什么会发生这种情况?
在实际请求发送之前,客户端会生成一个预检请求!预检请求在其 `Access-Control-Request-*` 标头中包含有关我们即将发送的实际请求的信息🔥
这为服务器提供了有关浏览器正在尝试发出的实际请求的信息:请求的**方法**是什么、**附加标头**是什么,等等。
服务器收到此预检请求,并发送一个带有服务器 CORS 标头的空 HTTP 响应!浏览器收到预检响应(除 CORS 标头外不包含任何数据),并检查是否应允许 HTTP 请求!✅
如果是这种情况,浏览器就会向服务器发送实际请求,然后服务器会使用我们要求的数据进行响应!
但是,如果不是这种情况,CORS 将阻止预检请求,并且实际请求永远不会被发送 ✋🏼 预检请求是一种很好的方法,可以防止我们访问或修改尚未启用任何 CORS 策略的服务器上的资源!服务器现在可以免受潜在有害跨源请求的影响 😃
💡 为了减少往返服务器的次数,我们可以通过在 CORS 请求中添加 `Access-Control-Max-Age` 标头来缓存预检响应!我们可以通过这种方式缓存预检响应,浏览器可以使用它,而不必发送新的预检请求!
🍪 凭证
默认情况下,Cookie、授权标头和 TLS 证书仅在同源请求中设置!但是,我们可能希望在跨源请求中使用这些凭据。也许我们想在请求中包含服务器可以用来识别用户的 Cookie!
尽管 CORS 默认不包含凭据,但我们可以通过添加 `Access-Control-Allow-Credentials` CORS 标头来更改这种情况!🎉
如果我们想要在跨域请求中包含 cookies 和其他授权标头,我们需要在请求中将 `withCredentials` 字段设置为 `true`,并在响应中添加 `Access-Control-Allow-Credentials` 标头。
一切就绪!我们现在可以在跨域请求中包含凭据了 🥳
虽然我认为我们都同意 CORS 错误有时会令人沮丧,但令人惊奇的是,它使我们能够在浏览器中安全地进行跨域请求(它应该得到更多的喜爱哈哈)✨
显然,关于同源策略和 CORS 的内容远不止我在本博文中介绍的那么简单!幸运的是,如果你想了解更多内容,可以查阅很多很棒的资源,比如这个或 W3 规范 💪🏼