启用跨源资源共享(CORS)的终极指南

2023年4月19日前端开发评论62,765字数 4724阅读15分44秒阅读模式

Ethan_2023-04-17_10-19-51

考虑以下情况:您正在尝试使用 fetch() 从网站上的 API 获取一些数据,但最终出现了错误。

您打开控制台并看到红色文本中写着“请求资源未包含Access-Control-Allow-Origin标头”或者“Access-Control-Allow-Origin标头的值 <some_url> 不等于提供的来源”,这表明您的请求被CORS策略阻止。

听起来很熟悉吗?在StackOverflow上发布了超过10,000个带有cors标签的问题,它是困扰前端和后端开发人员最常见的问题之一。

那么,什么是CORS策略,为什么我们经常遇到此错误呢?

什么是跨源资源共享 (CORS)?

有趣的是,这并不是我们描述的错误,而是预期的行为。

我们的网络浏览器强制执行同源策略,该策略限制不同源之间的资源共享。 跨源资源共享 或 CORS 是我们克服这一障碍的机制。 要了解 CORS,首先让我们了解同源策略及其需求。

同源策略

简单来说,同源策略是浏览器实现的“不与陌生人交谈”的网络版。

今天所有现代的网络浏览器都遵循同源策略,该策略限制了一个来源的XMLHttpRequest和fetch请求如何与另一个来源的资源进行交互。那么什么是“来源”呢?

它由方案、域名和端口组成。方案可以是HTTP、HTTPS、FTP或其他任何协议。类似地,端口也可以是任何有效的端口号。同源请求基本上就是这些请求中方案、域名和端口匹配的请求。让我们看下面这个例子。

假设我们的来源为http://localhost:3000,则请求可分为同源或跨域请求:

请求类型 原因
http://localhost:3000/about 同源 路径“/about”不被视为来源的一部分
http://localhost:3000/shop/product.html 同源 路径“/shop/product.html”不被视为来源的一部分
http://localhost:5000 跨域 不同的端口(5000 而不是 3000)
https://localhost:3000 跨域 不同的方案(HTTPS 而不是 HTTP)
https://www.shejibiji.com 跨域 不同的方案、域和端口

这就是为什么在开发单页应用程序 (SPA) 时,运行在 http://localhost:3000 上的前端无法对运行 http://localhost:5000 的服务器或任何其他端口进行 API 调用的原因。

此外,从来源 “https://mywebsite.com” 到来源 “https://api.mywebsite.com” 的请求仍然被视为跨站点请求,即使第二个来源是子域名。

由于同源策略,浏览器会自动阻止跨源请求的响应与客户端共享。 出于安全原因,这很棒!

但并非所有网站都是恶意的,在多种情况下您可能需要从不同来源获取数据,尤其是在现代微服务架构,其中不同的应用程序托管在不同的源上。

对于我们深入研究 CORS 并学习如何使用它来允许跨源请求来说,这是一个很好的转折点。

通过 CORS 解决跨域问题

我们已经确定浏览器不允许不同来源之间的资源共享,但是有无数的例子让我们能够这样做。 该怎么做呢? 这就是 CORS 发挥作用的时候了。

CORS 是一种基于 HTTP 标头的协议,可实现不同来源之间的资源共享。

除了 HTTP 标头,CORS 还依赖浏览器的预检请求,使用“OPTIONS”方法处理非简单请求。

本文后面将详细介绍简单请求和预检请求。

因为 HTTP 标头是 CORS 机制的关键,让我们看看这些标头以及它们各自的含义。

Access-Control-Allow-Origin

Access-Control-Allow-Origin 响应标头可能是 CORS 机制设置的最重要的 HTTP 标头。 此标头的值由允许访问资源的来源组成。 如果响应标头中不存在此标头,则表示服务器上尚未设置 CORS。

如果存在此标头,则会根据请求标头的“Origin”标头检查其值。 如果值匹配,则请求将成功完成并共享资源。

不匹配时,浏览器将响应 CORS 错误。

在公共 API 的情况下,要允许所有来源访问资源,可以在服务器上将“Access-Control-Allow-Origin”标头设置为“*”。 为了限制只有特定来源访问资源,可以将标头设置为客户端来源的完整域,例如 https://mywebsite.com

Access-Control-Allow-Methods

Access-Control-Allow-Methods 响应头用于指定允许的HTTP方法或HTTP方法列表,例如 GETPOSTPUT,服务器可以响应这些方法。

该头在预检请求的响应中存在。如果您的请求的HTTP方法不在允许方法列表中,将导致CORS错误。 当您想要限制用户通过“POST”、“PUT”、“PATCH”或“DELETE”请求修改数据时,这非常有用。

Access-Control-Allow-Headers

Access-Control-Allow-Headers”响应头指示请求可以具有的允许HTTP标头的列表。为支持自定义标头,例如“x-auth-token”,您可以相应地在服务器上设置CORS。

除了允许的标头之外的其他标头构成的请求将导致CORS错误。与“Access-Control-Allow-Methods”标头类似,“Access-Control-Allow-Headers”标头用于响应预检请求。

Access-Control-Max-Age

预检请求要求浏览器首先使用“OPTIONS” HTTP方法向服务器发出请求。只有在被视为安全的情况下才能发出主请求。但是,为每个预检请求进行“OPTIONS”调用可能是昂贵的。

为了防止这种情况,服务器可以使用“Access-Control-Max-Age”标头进行响应,允许浏览器将预检请求的结果缓存一段时间。该标头的值是以增量秒为单位的时间量。

总体而言,以下是CORS响应头的语法:

Access-Control-Allow-Origin: <allowed_origin> | *
Access-Control-Allow-Methods: <method> | [<method>]
Access-Control-Allow-Headers: <header> | [<header>]
Access-Control-Max-Age: <delta-seconds>

简单请求与预检请求

不触发CORS预检的请求属于简单请求。但是,在被视为简单请求之后,请求必须满足一些条件。这些条件是:

  1. 请求的HTTP方法应为以下之一:“GET”,“POST”或“HEAD”。
  2. 请求标头应仅由CORS安全列出的标头组成,例如“Accept”,“Accept-Language”,“Content-Language”和“Content-Type”,除了用户代理自动设置的标头。
  3. Content-Type”标头应仅具有以下三个值之一:“application/x-www-form-urlencoded”,“multipart/form-data”或“text/plain”。
  4. 如果使用“XMLHttpRequest”,则不应在由“XMLHttpRequest.upload”属性返回的对象上注册事件侦听器。
  5. 请求中不应使用“ReadableStream”对象。

如果未能满足这些条件中的任何一个,则请求被视为预检请求。对于这种请求,浏览器必须首先使用“OPTIONS”方法向不同的源发送请求。

这用于检查实际请求是否安全可发送到服务器。实际请求的批准或拒绝取决于预检请求的响应标头。如果这些响应标头与主请求的标头不匹配,则不会发出请求。

启用CORS

让我们考虑我们面临CORS错误的初始情况。

我们可以根据我们是否可以访问托管资源的服务器来解决此问题。我们可以将其缩小为两种情况:

  1. 您可以访问后端或了解后端开发人员。
  2. 您只能管理前端,并且无法访问后端服务器。

如果您可以访问后端:

因为CORS只是基于HTTP头的机制,所以您可以配置服务器以响应适当的标头,以便在不同的来源之间启用资源共享。查看上面讨论的CORS标头,并相应地设置标头。

对于Node.js + Express.js开发人员,可以从npm安装cors中间件。这是一个使用Express Web框架和CORS中间件的代码片段:

const express = require('express');
const cors = require('cors');
const app = express();

app.use(cors());

app.get('/', (req, res) => {
  res.send('API running with CORS enabled');
});

app.listen(5000, console.log('Server running on port 5000'));

如果不传递由 CORS 配置组成的对象,将使用默认配置,相当于:

{
  "origin": "*",
  "methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
  "preflightContinue": false,
  "optionsSuccessStatus": 204
}

以下是如何在服务器上配置 CORS,它只允许来自 https://yourwebsite.com 的带有标头 Content-TypeAuthorizationGET 请求,预检缓存时间为 10 分钟:

app.use(cors({
  origin: 'https://yourwebsite.com',
  methods: ['GET'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  maxAge: 600
}));

虽然此代码特定于 Express.js 和 Node.js,但概念保持不变。 使用您选择的编程语言和框架,您可以使用您的响应手动设置 CORS 标头或为其创建自定义中间件。

如果你只能管理前端:

很多时候,我们可能无法访问后端服务器。 例如,公共 API。 因此,我们无法将标头添加到我们收到的响应中。 但是,我们可以使用代理服务器将 CORS 标头添加到代理请求中。

cors-anywhere 项目是一个 Node.js 反向代理,可以让我们做同样的事情。 代理服务器在 https://cors-anywhere.herokuapp.com/ 上可用,但您可以通过克隆存储库并将其部署在 等免费平台上来构建自己的代理服务器 Heroku 或任何其他所需的平台。

在这种方法中,不是像这样直接向服务器发出请求:

fetch('https://jsonplaceholder.typicode.com/posts');

只需将代理服务器的 URL 附加到 API URL 的开头,如下所示:

fetch('https://cors-anywhere.herokuapp.com/https://jsonplaceholder.typicode.com/posts');

总结 Conclusion

随着我们了解到同源策略在防范跨站请求伪造攻击方面的安全性,CORS 似乎变得更加合理。虽然控制台中红色 CORS 错误消息的发生并不会神奇地消失,但是您现在已经掌握了解决这些消息的知识,无论您是在前端还是后端工作。

翻译自:The ultimate guide to enabling Cross-Origin Resource Sharing (CORS)

2024最新 Vue.js 的一般性学习路径 前端开发

2024最新 Vue.js 的一般性学习路径

可以根据自己的经验水平和学习方式进行调整。 初学者: 了解前端基础: 熟悉HTML、CSS和JavaScript。 了解基本的编程概念和Web开发原理。 学习Vue.js核心概念: 官方文档:阅读Vu...
如何初次使用react及next.js框架 前端开发

如何初次使用react及next.js框架

官方文档地址:https://react.dev/learn 请确保你已经安装node.js,并可以使用npm或者npx命令。 (本教程默认你已经安装) 仅作本人学习记录,供参考。 安装及使用Reac...
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定