原文: https://dev.to/lydiahallie/cs-visualized-cors-5b8h 作者: Lydia Hallie 翻译: ChatGTP
偶尔,每个开发者都会在控制台中看到那个讨厌的大红色 Access to fetched has been blocked by CORS policy
错误!😬 尽管有一些快速消除此错误的方法,但今天我们不要掉以轻心!相反,让我们看看 CORS 到底在做什么,以及为什么它实际上是我们的朋友 👏🏼
❗️ 在本博文中,我不会解释 HTTP 基础知识。如果您想了解有关 HTTP 请求和响应的更多信息,我之前写过一篇小博文,尽管我在示例中使用的是 HTTP/1.1 而不是 HTTP/2,但这不会影响 CORS。
在前端,我们经常希望显示位于其他地方的数据!在我们可以显示这些数据之前,浏览器首先必须向服务器发出请求以获取数据!客户端发送带有服务器需要的所有信息的 HTTP 请求,以便将数据发送回客户端 🙂
假设我们正在尝试从位于 api.website.com
的服务器上获取一些用户信息,以在我们的 www.mywebsite.com
网站上显示!
太好了!😃 我们刚刚向服务器发送了一个 HTTP 请求,然后它以我们请求的 JSON 数据进行了响应。
让我们尝试相同的请求,但来自另一个域。我们不再从 www.mywebsite.com
发出请求,而是从位于 www.anotherdomain.com
的网站发出请求。
等等,什么?我们发送了完全相同的请求,但这次浏览器显示了一个奇怪的错误?
我们刚刚看到了 CORS 的作用!💪🏼 让我们看看为什么会发生这个错误,以及它到底意味着什么。
网络实施了一种称为同源策略的机制。默认情况下,我们只能访问与我们请求的起源相同的资源!💪🏼 例如,加载位于 https://mywebsite.com/image1.png
的图像是完全可以的。
当资源位于不同的(子)域、协议或端口时,资源就是跨源的!
好了,但是为什么同源策略要存在呢?
假设同源策略不存在,你不小心点击了你阿姨在Facebook上发给你的许多病毒链接之一。该链接将您重定向到一个带有嵌套的 iframe 的“邪恶网站”,该 iframe 加载了您银行的网站,并通过一些已设置的 cookie 成功登录了您!😬
这个“邪恶网站”的开发者使得网站能够访问此 iframe 并与您银行网站的 DOM 内容交互,以便代表您向他们的帐户发送资金!
是的... 这是一个巨大的安全风险!我们不希望任何人都能够随意访问一切 😧
幸运的是,同源策略在这里帮了我们!该策略确保我们只能访问相同源的资源。
在这种情况下,源 www.evilwebsite.com
尝试访问 www.bank.com
的跨源资源!同源策略阻止了这种情况发生,并确保邪恶网站的开发人员不能随意访问我们的银行数据 🥳
好的,那么... 这与 CORS 有什么关系呢?
尽管同源策略实际上仅适用于脚本,但浏览器为 JavaScript 请求“扩展”了此策略:默认情况下,我们只能访问相同源的获取的资源!
嗯,但是... 我们经常需要访问跨源资源 🤔 也许我们的前端需要与后端 API 交互以加载数据?为了安全地允许跨源请求,浏览器使用一种称为CORS的机制!🥳
CORS 代表跨源资源共享。尽管浏览器禁止我们访问未位于相同源的资源,但我们可以使用 CORS 稍微修改这些安全限制,同时确保我们安全地访问这些资源 🎉
用户代理(例如浏览器)可以使用 CORS 机制,以根据 HTTP 响应中特定 CORS 头的值允许跨源请求,否则这些请求将被阻止!✅
当发出跨源请求时,客户端会自动向我们的 HTTP 请求添加额外的头部:Origin
。Origin
头的值是请求的起源!
为了让浏览器允许访问跨源资源,它期望从服务器响应中获得某些头部,这些头部指定此服务器是否允许跨源请求!
作为服务器开发者,我们可以通过向 HTTP 响应添加额外的头部来确保允许跨源请求,所有这些头部都以 Access-Control-* 开头 🔥 根据这些 CORS 响应头的值,浏览器现在可以允许某些本来会被同源策略阻止的跨源响应!
尽管我们可以使用多个 CORS 头部,但有一个头部是浏览器需要以允许访问跨源资源的:Access-Control-Allow-Origin!🙂 该头部的值指定允许访问资源的起源。
如果我们正在开发一个应该允许 https://mywebsite.com
访问的服务器,我们可以将该域的值添加到Access-Control-Allow-Origin头部!
🎉 太棒了!这个头部现在已经添加到服务器发送回客户端的响应中。通过添加这个头部,同源策略将不再限制我们接收位于 https://api.mywebsite.com
起源的资源,如果我们是从 https://mywebsite.com
发送请求的话!
浏览器中的 CORS 机制会检查 Access-Control-Allow-Origin
头部的值是否等于请求发送的 Origin
的值 🤚🏼
在这种情况下,我们请求的起源是 https://www.mywebsite.com
,它在 Access-Control-Allow-Origin
响应头部中有记录!
太好了!🎉 我们成功地接收了跨源资源!那么当我们尝试从未在 Access-Control-Allow-Origin
头部中列出的起源访问这些资源时会发生什么呢?🤔
啊,是的,CORS 抛出了有时候令人沮丧的臭名昭著的错误!但现在我们实际上看到它是完全有道理的
'Access-Control-Allow-Origin' 头部的值 'https://www.mywebsite.com' 不等于 提供的起源。
在这种情况下,提供的起源是 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 有两种类型的请求:简单请求 和 预检 请求。请求是简单还是预检取决于请求中的一些值(别担心,你不必记住这些 lol)。
当请求是 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 头部来更改这一点!🎉
如果我们想要在跨源请求中包含 Cookie 和其他授权头,我们需要在请求上将 withCredentials
字段设置为 true
,并在响应中添加 Access-Control-Allow-Credentials
头部。
搞定了!我们现在可以在跨源请求中包含凭据了 🥳
虽然我认为我们都可以一致同意,CORS 错误有时可能让人沮丧,但它确实使我们能够在浏览器中安全地进行跨源请求(它应该得到更多的关注 lol) ✨
显然,同源策略和 CORS 还有很多内容,我在这篇博文中无法涵盖所有!幸运的是,有许多出色的资源,比如这个,或者 W3 规范,如果你想要更多阅读 💪🏼
而且,如常,随时与我联系!😊