什么是 CORS?
在本文中,我将解释什么是 CORS 和 CORS 错误,以及为什么您可能会遇到它们。我将提供可能的解决方案,并解释什么是预检请求、CORS 标头,以及它们在多方通信中的重要性。本文假设您具备一些 Web 开发和 HTTP 协议的基础知识。我力求以初学者易于理解的方式撰写本文,并填充相关知识,同时尽量避免过多与 CORS 主题无关的技术细节。如果您发现任何错误或有任何建议,请随时与我联系。在某些地方,我进行了简化,将服务定义为服务器,反之亦然。
什么是 CORS?
跨域资源共享 (CORS) 是一种基于 HTTP 的安全机制,由客户端(Web 浏览器)控制和强制执行。它允许服务(API)指定客户端可以从其自身以外的任何来源请求资源。CORS 的设计旨在响应同源策略(SOP),该策略限制了由一个来源加载的网站(HTML 文档或 JS 脚本)如何与来自另一个来源的资源进行交互。CORS 用于明确允许某些跨域请求,同时拒绝其他请求。
CORS 主要在 Web 浏览器中实现,但也可以在 API 客户端中使用。它存在于所有流行的 Web 浏览器中,例如 Google Chrome、Firefox、Opera 和 Safari。该标准已于2014 年 1 月被接受为 W3C 推荐标准。基于此,我们可以假设它已在所有当前可用以及未列出的 Web 浏览器中实现。
它是如何工作的?
一切始于客户端,在发送主请求之前。客户端向服务发送 CORS 预检请求,以获取 HTTP 标头(CORS 标头)中包含参数的资源。服务使用相同的标头进行响应,但值可能相同或不同。客户端根据 CORS 预检响应决定是否可以向服务发送主请求。如果响应不符合 CORS 预检的要求,Web 浏览器(客户端)将抛出错误。
无论使用什么库或框架从 Web 浏览器发送请求,CORS 预检请求都会发送。因此,您在后端应用程序使用 API 时无需遵循 CORS 要求。
CORS 不会阻止用户请求或下载资源。您仍然可以使用curl、Insomnia或Postman 等
应用程序成功请求资源。只有在 CORS 策略不允许的情况下,CORS 才会阻止浏览器访问资源。
什么是 CORS 预检?
当浏览器向服务器发送请求时,它首先会发送一个HTTP 选项请求。这被称为 CORS 预检请求。然后,服务器会返回一个允许的方法和标头列表作为响应。如果浏览器被允许发出实际请求,则会发送实际请求。如果不允许,则会显示错误并且不会继续发送主请求。
CORS 标头
CORS 标头是用于控制 CORS 策略的常规 HTTP 标头。它们用于浏览器向服务器发送 CORS 预检请求,服务器响应以下内容:
Access-Control-Allow-Origin
指示哪个来源可以获取资源。请使用一个或多个来源,例如:https://foo.io,http://bar.io
。Access-Control-Allow-Methods
指示允许的 HTTP 方法。请使用一个或多个逗号分隔的 HTTP 方法,例如:GET,PUT,POST
。Access-Control-Allow-Headers
指示允许哪些请求标头。请使用一个或多个标头,例如:Authorization,X-My-Token
。Access-Control-Allow-Credentials
指示是否允许发送 Cookie。默认值:false
。Access-Control-Max-Age
- 指示请求结果应缓存多长时间,以秒为单位。默认值:0
。
如果您决定使用Access-Control-Allow-Credentials=true
,则需要注意标头*
中不能使用通配符Access-Control-Allow-*
。需要明确列出所有允许的来源、方法和标头。
查看CORS 标头的完整列表。
为什么请求被 CORS 策略阻止?
如果你是一名 Web 开发者,你可能已经见过或听说过 CORS 错误,并且已经多次在 Google 上搜索解决方案。最常见的问题是浏览器由于 CORS 策略而阻止了请求。浏览器会抛出错误并在控制台中显示日志:
Access to XMLHttpRequest at 'http://localhost:8080/' from origin
'http://localhost:3000' has been blocked by CORS policy:
Response to preflight request doesn't pass access control
check: No 'Access-Control-Allow-Origin' header is present
on the requested resource.
上述 CORS 错误通知用户,浏览器无法https://localhost:8080
从某个来源 ( )访问资源 ( https://localhost:3000
),因为服务器不允许。发生这种情况的原因是,服务器未在 CORS 预检响应中返回包含Access-Control-Allow-Origin
来源的标头或通配符。*
请求不仅可能因为来源不正确而被 CORS 策略阻止,还可能因为 HTTP 标头、HTTP 方法或 Cookie 标头不正确而被阻止。
如何修复 CORS 错误?
“修复 CORS” 的基本思路是使用正确的标头响应客户端发送的“OPTIONS”请求。有很多方法可以开始使用正确的 CORS 进行响应。您可以使用代理服务器,也可以在服务器中使用中间件。
请记住,Access-Control-*
标头会根据Access-Control-Max-Age
标头中设置的值缓存在 Web 浏览器中。请确保在测试更改之前清除缓存。您也可以在浏览器中禁用缓存。
1.配置您的服务器
默认情况下,如果您是服务器所有者,则需要在服务器中配置 CORS 响应,这是正确解决此问题的唯一方法。您可以通过多种方式在应用程序的多个层级上实现此操作。最常见的方法是使用反向代理、API 网关或任何其他提供向响应添加标头的路由服务。您可以使用许多服务来实现此目的,其中包括:HAProxy、Linkerd、Istio、Kong、nginx、Apache 和 Traefik。如果您的基础架构仅包含一个应用程序,而没有任何其他层级,那么您只需在应用程序代码中添加 CORS 支持即可。
以下是一些启用 CORS 的常见示例:
- Apache:修改.htaccess文件,
- Nginx:修改配置文件,
- Traefik:使用中间件,
- Spring Boot:使用
@EnableCORS
注解, - ExpressJS:使用
app.use(cors())
, - NextJS:使用请求助手。
您可以在这里找到更多在不同框架和语言中启用 CORS 的示例:enable-cors.org。
如果您无法在服务中启用 CORS,但仍想向其发出请求,则需要使用下面描述的解决方案之一。
2. 安装浏览器扩展
使用浏览器扩展程序或许是解决开发者环境中 CORS 问题的一种快速简便的方法。使用浏览器扩展程序的最大优势在于您无需更改代码或配置。另一方面,您需要在每个用于测试 Web 应用程序的 Web 浏览器上安装扩展程序。
浏览器扩展程序会通过添加必要的标头来欺骗浏览器,从而修改传入的预检请求。这是一个便捷的解决方案,可以在本地使用仅接受来自生产域的请求的生产 API。
您可以在Google 网上应用店或Mozilla 附加组件库中找到扩展程序。在某些情况下,默认扩展程序配置可能不够用;请确保已安装的扩展程序配置正确。您还应注意,无限期地启用扩展程序可能会导致某些网站出现问题。建议仅将它们用于开发目的。
3. 禁用浏览器 CORS 检查
您可以在浏览器中完全禁用 CORS 检查。要在 Google Chrome 中禁用 CORS 检查,您需要关闭浏览器,然后使用--disable-web-security
和--user-data-dir
标志启动浏览器。这样,Google Chrome 将不会发送 CORS 预检请求,也不会验证 CORS 标头。
# Windows
chrome.exe --user-data-dir="C://chrome-dev-disabled-security" --disable-web-security --disable-site-isolation-trials
# macOS
open /Applications/Google\ Chrome.app --args --user-data-dir="/var/tmp/chrome-dev-disabled-security" --disable-web-security --disable-site-isolation-trials
# Linux
google-chrome --user-data-dir="~/chrome-dev-disabled-security" --disable-web-security --disable-site-isolation-trials
以上所有命令都会在隔离沙盒中启动 Google Chrome。它们不会影响您的主 Chrome 配置文件。
4. 设置代理服务器
如果您正在寻找一种无需更改浏览器设置的解决方案,那么您应该考虑代理服务器解决方案。此选项可帮助您克服 CORS 错误,而无需更改浏览器本身的任何内容。使用代理服务器的理念是将所有请求发送到您的服务器,然后服务器将请求发送到您想要使用的实际服务。您可以使用您选择的语言和框架自行构建代理服务器。您需要配置 CORS 并实现将收到的请求传递到其他服务的功能。
如果您无法访问想要使用的服务,代理服务器是一个不错的解决方案。市面上有现成的开源代理服务器服务,但您应始终确保它们不会试图使用授权标头拦截您的请求并将其传递给任何第三方服务。此类安全漏洞可能会对您以及该服务的潜在用户造成灾难性的后果。
您可以在互联网上找到的开源 CORS 服务列表:
- https://github.com/Freeboard/thingproxy
- https://github.com/bulletmark/corsproxy
- https://github.com/Rob--W/cors-anywhere
在使用任何这些服务之前,请先查看最新版本的代码。
如何测试 CORS?
使用浏览器测试 CORS 配置可能比较繁琐。您可以使用CORS Tester、test-cors.org之类的工具,或者,如果您熟悉命令行,也可以使用curl来测试 CORS 配置。
curl -v -X OPTIONS https://simplelocalize.io/api/v1/translations
警惕虚假的 CORS 错误
在某些情况下,当服务位于带有速率限制器、负载均衡器或授权服务器的附加层之后时,您可能会收到错误的 CORS 错误。服务器阻止或拒绝的请求应返回错误状态码。例如:
- 401 未经授权,
- 403 禁止访问,
- 429 请求过多,
- 500 内部服务器错误,
- 2XX 或 3XX 以外的任何数字。
您可能会看到请求由于预检请求失败而被阻止,但实际上,服务只是拒绝了它。您应该始终检查响应的状态码和响应主体,以避免不必要的调试。当 CORS 预检请求失败时,浏览器会正确发出警报,但失败的原因不一定与 CORS 配置有关。
结论
在本文中,我尝试解释什么是 CORS,以及它最常见的问题是什么。我提出了 4 种解决 CORS 问题的方法,并解释了每种方法的优缺点。我还解释了如何正确配置 CORS 响应以及如何测试它们。此外,我还展示了识别 CORS 错误时最常见的问题。我尽量用简单的术语来解释,避免使用技术细节。如果您有任何问题、疑问或建议,请随时联系我。
编码愉快!
资源
- https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors
- https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy
- https://enable-cors.org/
- https://stackoverflow.com/a/42024918/1133169