15 张精美动图全面讲解 CORS_动图百科
liuian 2025-09-18 03:44 18 浏览
作者:Lydia Hallie
转发链接:
https://dev.to/lydiahallie/cs-visualized-cors-5b8h?utm_campaign=React%2BNative%2BNow&utm_medium=web&utm_source=React_Native_Now_69#cs-cors
前言:
本文翻译自 Lydia Hallie 小姐姐写的 CS Visualized: CORS,她用了大量的动图去解释 CORS 这个概念,国内还没有人翻译本文,所以我在原文的理解上翻译了本文并修改了一些错误,希望能帮到大家。
觉得翻译的不错一定要点赞哦,谢谢你,这对我真的很重要!
注:原文的动图均为 keynote 制作
前端开发中,我们经常要使用其他站点的数据。前端显示这些数据之前,必须向服务器发出请求以获取该数据。
假设我们正在访问 https://api.mywebsite.com 这个站点,点击按钮向
https://api.mywebsite.com/users 发送请求,获取网站上的一些用户信息:
:这里原作者有个笔误,把 https://api.mywebsite.com 误写为 https://www.mywebsite.com 了,图中也有这个错误,读者要注意一下不要被误导
从结果上看表现非常完美,我们向服务器发送请求,服务器返回了我们需要的 JSON 数据,前端也正常的渲染出了结果。
下面我们换一个网站试试。用
https://www.anotherwebsite.com 这个网站向
https://api.website.com/users 发送请求:
问题来了,我们请求同样的接口网站,但是这次浏览器给我们抛出一个 Error。
刚刚浏览器抛出的就是 CORS Error,下面让我们分析一下为什么会产生这种 Error,以及这个 Error 的确切含义是什么。
1.同源策略
浏览器网络请求时,有一个同源策略的机制。即默认情况下,使用 API 的 Web 应用程序只能从加载应用程序的同一个域请求 HTTP 资源。
比如说, https://www.mywebsite.com 请求
https://www.mywebsite.com/page 是完全没有问题的。但是当资源位于不同协议、子域或端口的站点时,这个请求就是跨域的。
目前来看,同源策略会让三种行为受限:
- Cookie、LocalStorage 和 IndexDB 访问受限
- 无法操作跨域 DOM(常见于 iframe)
- Javascript 发起的 XHR 和 Fetch 请求受限
那么,为什么会存在同源策略呢?
我们做个假设,如果不存在同源策略,你无意中点击了七大姑在微信上给你发的一篇养生文章链接。其实这个网页是个钓鱼网站,访问链接后就把你重定向到一个嵌入了 iframe 的攻击网站,这个 iframe 会自动加载银行网站,并通过 cookies 登录你的账户。
登陆成功后,这个钓鱼网站还可以控制 iframe 的 DOM,通过一系列骚操作把你卡里的钱转走。
这是一个非常严重的安全漏洞,我们不希望自己在互联网的内容被随便访问,更不要说这种涉及到钱的网站了。
同源策略可以帮助我们解决这个安全问题,这个策略确保我们只能访问同一站点的资源。
在这种情况下,
https://www.evilwebsite.com 尝试跨站访问 https://www.bank.com 的资源,同源策略就会阻止这个操作,让钓鱼网站无法访问银行网站的数据。
说了这么多,同源策略和 CORS 又有什么关系?
2.浏览器 CORS
出于安全原因,浏览器限制从脚本内发起的跨域 HTTP 请求。 例如 XHR 和 Fetch 就遵循同源策略。这意味着使用 API 的 Web 应用程序只能从加载应用程序的同一个域请求 HTTP 资源。
日常的业务开发中,我们会经常访问跨域资源,为了安全的请求跨域资源,浏览器使用一种称为 CORS 的机制。
CORS 的全名是 Cross-Origin Resource Sharing,即跨域资源共享。尽管默认情况下浏览器禁止我们访问跨域资源,但是我们可以利用 CORS 放宽这种限制,在保证安全性的前提下访问跨域资源。
浏览器可以利用 CORS 机制,放行符合规范的跨域访问,阻止不合规范的跨域访问。浏览器内部是怎么做的呢?我们下面就来分析一下。
Web 程序发出跨域请求后,浏览器会自动向我们的 HTTP header 添加一个额外的请求头字段:Origin。Origin 标记了请求的站点来源:
GET https://api.website.com/users HTTP/1/1
Origin: https://www.mywebsite.com // <- 浏览器自己加的为了使浏览器允许访问跨域资源, 服务器返回的 response 还需要加一些响应头字段,这些字段将显式表明此服务器是否允许这个跨域请求。
3.服务端 CORS
作为服务器开发人员,我们可以通过在 HTTP 响应中添加额外的响应头字段 Access-Control-* 来表明是否允许跨域请求。根据这些 CORS 响应头字段,浏览器可以允许一些被同源策略限制的跨源响应。
虽然有好几个 CORS 响应头字段,但有一个字段是必加的,那就是
Access-Control-Allow-Origin。这个头字段的值指定了哪些站点被允许跨域访问资源。
1 如果我们有服务器的开发权限,我们可以给 https://www.mywebsite.com 加上访问权限:将该域添加到
Access-Control-Allow-Origin 中。
这个响应头字段现在被添加到服务器发回给客户端的 response header 中。这个字段添加后,如果我们从 https://www.mywebsite.com 发送跨域请求,同源策略将不再限制 https://api.mywebsite.com 站点返回的资源。
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://www.mywebsite.com
Date: Fri, 11 Oct 2019 15:47 GM
Content-Length: 29
Content-Type: application/json
Server: Apache
{user: [{...}]}2 收到服务器返回的 response 后,浏览器中的 CORS 机制会检查
Access-Control-Allow-Origin 的值是否等于 request 中 Origin 的值。
在这个例子中,request 的 Origin 是 https://www.mywebsite.com,这和 response 中
Access-Control-Allow-Origin 的值是一样的:
3 浏览器校验通过,前端成功地接收到跨域资源。
那么,当我们试图从一个没有在
Access-Control-Allow-Origin 中列出的网站跨域访问这些资源会发生什么呢?
如上图所示,从
https://www.anotherwebsite.com 跨域访问 https://api.mywebsite.com 资源,浏览器抛出一个 CORS Error,经过上面的讲解,我们可以读懂这个报错信息了:
The 'Access-Control-Allow-Origin' header has a value
'https://www.mywebsite.com' that is not equal
to the supplied origin. 在这种情况下,Origin 的值是
https://www.anotherwebsite.com。然而,服务器在
Access-Control-Allow-Origin 响应头字段中没有标记这个站点,浏览器 CORS 机制就阻止了这个响应,我们无法在我们的代码中获取响应数据。
CORS 还允许我们添加通配符 * 作为允许的外域,这意味着该资源可以被任意外域访问,所以要注意这种特殊情况
Access-Control-Allow-Origin 是 CORS 机制提供的众多头字段之一。服务器开发人员还可以通过其它头字段扩展服务器的 CORS 策略,以允许/禁止某些请求。
另一个常见的响应头字段是
Access-Control-Allow-Methods。其指明了跨域请求所允许使用的 HTTP 方法。
在上图的案例中,只有GET,POST 或 PUT 方法被允许跨域访问资源。其他 HTTP 方法,例如 PATCH 和 DELETE 都会被阻止。
如果您想知道其它的 CORS 响应头字段是什么以及它们的用途,可以查看此列表。
说到PUT,PATCH 和 DELETE 这几个 HTTP 方法,CORS 处理这些方法时还有些不同。这些非简单请求会触发 CORS 的预检请求。
4.预检请求
CORS 有两种类型的请求:一种是简单请求(simple request),一种是预检请求(preflight request)。一个跨域请求到底是简单的的还是预见的,取决于一些 request header。
当请求是 GET 或 POST 方法并且没有任何自定义 Header 字段时,一般来说就是个简单请求。除此之外的任何请求,诸如 PUT,PATCH 或 DELETE 方法,将会产生预检。
如果你想知道一个请求必须满足哪些要求才能成为简单请求,可以查看 MDN 简单请求相关的文档。
说了这么多,「预检请求」到底是什么意思?下面我们就来探讨一下。
1 在发送实际请求之前,客户端会先使用 OPTIONS 方法发起一个预检请求,预检请求的 Access-Control-Request-* 中包含有关我们将要处理的实际请求的信息:
- 首部字段 Access-Control-Request-Method 告知服务器,实际请求要用到的方法是什么
- 首部字段 Access-Control-Request-Headers 告知服务器,实际请求将附带的自定义请求首部字段是什么
OPTIONS https://api.mywebsite.com/user/1 HTTP/1.1
Origin: https://www.mywebsite.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type2 服务器接收到预检请求后,会返回一个没有 body 的 HTTP 响应,这个响应标记了服务器允许的 HTTP 方法和 HTTP Header 字段:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://www.mywebsite.com
Access-Control-Request-Method: GET POST PUT
Access-Control-Request-Headers: Content-Type3 浏览器收到预检响应,并检查是否应允许发送实际请求。
:上图预检响应漏了
Access-Control-Allow-Headers: Content-Type
4 如果预检响应检测通过,浏览器会将实际请求发送到服务器,然后服务器返回我们需要的资源。
如果预检响应没有检验通过,CORS 会阻止跨域访问,实际的请求永远不会被发送。预检请求是一种很好的方式,可以防止我们访问或修改那些没有启用 CORS 策略的服务器上的资源。
为了减少网络往返次数,我们可以通过在 CORS 请求中添加 Access-Control-Max-Age 头字段来缓存预检响应。浏览器可以使用缓存来代替发送新的预检请求。
5.认证
XHR 或 Fetch 与 CORS 的一个有趣的特性是,我们可以基于 Cookies 和 HTTP 认证信息发送身份凭证。一般而言,对于跨域 XHR 或 Fetch 请求,浏览器不会发送身份凭证信息。
尽管 CORS 默认情况下不发送身份凭证,但我们可以通过添加
Access-Control-Allow-Credentials CORS 响应头来更改它。
如果要在跨域请求中包含 cookie 和其他授权信息,我们需要做以下操作:
- XHR 请求中将 withCredentials 字段设置为 true
- Fetch 请求中将 credentials 设为 include
- 服务器把 Access-Control-Allow-Credentials: true 添加到响应头中
// 浏览器 fetch 请求
fetch('https://api.mywebsite,com.users', {
credentials: "include"
})
// 浏览器 XHR 请求
let xhr = new XMLHttpRequest();
xhr.withCredentials = true;
// 服务器添加认证字段
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true把上面的工作做好后,我们就可以在跨域请求中包含身份凭证信息了。
6.总结
CORS Error 一定程度上会让前端开发很头疼,但是遵循它的相关规定后,它可以让我们在浏览器中进行安全的跨域请求。
同源策略和 CORS 的知识点有很多,本文只讲了一些关键知识点,如果你想全面学习 CORS 的相关知识,我推荐你查阅MDN 文档和 W3C 规范,这些一手知识是最准确的。
推荐
这篇文章就到此结束了,如果觉得不错的话一定要点赞鼓励一下哦,祝大家学习进步,工作顺利!
作者:Lydia Hallie
转发链接:
https://dev.to/lydiahallie/cs-visualized-cors-5b8h?utm_campaign=React%2BNative%2BNow&utm_medium=web&utm_source=React_Native_Now_69#cs-cors
相关推荐
- win7正版怎么下载(我告诉你怎么下载win7)
-
如题,题主想在红警之家下载红警,很简单,可以用迅雷下载软件下载,下载以后用解压软件解压就行了,但是红警这款软件在winxp系统下,是最理想的,7系统下面会出现闪退的情况。下载很容易的,闪退的时候,设置...
- 电脑关机脚本bat命令(win10关机bat脚本)
-
bat关机命令需要使用文本文档。使用文本文档制作bat关机命令步骤如下所示:1、在电脑桌面空白处点击鼠标右键。2、在鼠标右键菜单中,选择新建文本文档。3、点击打开文本文档。4、在新建文本文档中输入关机...
- fat32格式是什么意思(u盘fAT32格式是什么意思)
-
是一种分区格式。这种格式使用32位文件分配表,大大提高了磁盘的管理能力,打破了每个分区只有4GB的FAT16限制。对于使用FAT32文件系统的每个逻辑盘内部空间又可划分为三部分,依次是引导区(BOO...
-
- 手机系统更新软件(手机系统更新软件不更新会怎样)
-
第一步:打开苹果手机的设置,点击“通用”选项进入。第二步:选择“软件更新”选项进入第三步:在软件更新界面,如果有新的更新,点击“下载并安装”即可!应用商店里一键更新。在手机软件里,为了更新最新版本可以进软件商店里面找一下,更新点一下,然后它...
-
2026-01-14 09:37 liuian
- 联想小新如何重装系统(联想小新重装系统怎么操作)
-
联想小新重装系统,方法∶在关机状态下,按下电脑的一键恢复按钮(需确保笔记本屏盖在打开状态)。首先找到按键孔,用针对准插孔,捅一下,电脑启动,进入启动选择界面,选择systemrecovery,按回车...
- 麦克风没声音(win11麦克风没声音)
-
一.先确保你的麦克风能正常使用。请确保麦克风本身是好的,连接线没有问题,请确保你的测试软件已正确设置,如YY之类的软件。二.确认你的麦克风是否插入正确的插孔一般麦克风是插入红色插孔中。三.确认你...
- vs2015官网下载(vs2015 下载)
-
VisualStudio2015下载完成之后,会有一个名为“vs2015.pro_chs.iso”的光盘镜像文件。光盘镜像文件将光盘镜像文件在虚拟光驱中加载之后,可以打开查看光盘内容。安装文件双...
- u盘自我保护怎么解除(怎么样取消u盘的自我保护)
-
要解除U盘保护,首先插入U盘后打开“我的电脑”,右击选择U盘图标,点击“属性”。在弹出的对话框中,选择“安全”标签,然后点击“编辑”按钮,根据自己的需要选择或取消“对于系统用户完全控制”权限,点击“确...
- 如何设置自动关机win10(windows 10如何设置自动关机)
-
Win10设置自动关机,需要以下步骤:1.按“Win+R”组合键,呼出“运行”;2.将定时关机命令设置为“shutdown-s-t7200”;3.在“运行”内输入命令,点击“确定”即可;4.如果设置错...
- 公版驱动(公版驱动和专用驱动的区别)
-
公版这个名词特指显卡本身,与驱动无关。一般采用芯片制造商自己设计的显卡,称为公版。显卡驱动只有WHQL版本、Beta版本、兼容版、定制版之分。兼容版一般称为万能驱动,不会给显卡带来多少优化,只是让你能...
- nod32是什么软件(nod32是哪个国家的)
-
起源于捷克斯洛伐克总部现在美国下面是nod32的由来:nod是根据一部电视剧(城市边缘的医院)起的,原意是“磁盘边的医院”32是源于当16-bitNOD-ICE很成熟的时候32位处理器出来了升级适应3...
- 欧拉linux系统官网(欧拉系统命令)
-
在华为欧拉服务器上配置Linux网络,首先需要编辑网络配置文件,位于/etc/sysconfig/network-scripts目录下,根据网络需求配置对应的网络接口,IP地址、子网掩码、网关等信息,...
- deepin安装显卡驱动(deepin安装显卡驱动后无法进入图形界面)
-
1、首先必须使用rufus制作U盘启动,必须选择DD格式2、从其他linux镜像比如Ubantu或其他拷贝出EFI的引导文件,具体是镜像中的EFI—boot—grubx86.efi这个文件,把这个文件...
- 一周热门
-
-
飞牛OS入门安装遇到问题,如何解决?
-
如何在 iPhone 和 Android 上恢复已删除的抖音消息
-
Boost高性能并发无锁队列指南:boost::lockfree::queue
-
大模型手册: 保姆级用CherryStudio知识库
-
用什么工具在Win中查看8G大的log文件?
-
如何在 Windows 10 或 11 上通过命令行安装 Node.js 和 NPM
-
威联通NAS安装阿里云盘WebDAV服务并添加到Infuse
-
Trae IDE 如何与 GitHub 无缝对接?
-
idea插件之maven search(工欲善其事,必先利其器)
-
如何修改图片拍摄日期?快速修改图片拍摄日期的6种方法
-
- 最近发表
- 标签列表
-
- python判断字典是否为空 (50)
- crontab每周一执行 (48)
- aes和des区别 (43)
- bash脚本和shell脚本的区别 (35)
- canvas库 (33)
- dataframe筛选满足条件的行 (35)
- gitlab日志 (33)
- lua xpcall (36)
- blob转json (33)
- python判断是否在列表中 (34)
- python html转pdf (36)
- 安装指定版本npm (37)
- idea搜索jar包内容 (33)
- css鼠标悬停出现隐藏的文字 (34)
- linux nacos启动命令 (33)
- gitlab 日志 (36)
- adb pull (37)
- python判断元素在不在列表里 (34)
- python 字典删除元素 (34)
- vscode切换git分支 (35)
- python bytes转16进制 (35)
- grep前后几行 (34)
- hashmap转list (35)
- c++ 字符串查找 (35)
- mysql刷新权限 (34)
