前言:

本文翻译自 Lydia Hallie[1] 小姐姐写的 ✋???????? CS Visualized: CORS[2],她用了大量的动图去解释 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 添加一个额外的请求头字段:OriginOrigin 标记了请求的站点来源:

GET https://api.website.com/users HTTP/1/1
Origin: https://www.mywebsite.com // <- 浏览器自己加的

为了使浏览器允许访问跨域资源, 服务器返回的 response 还需要加一些响应头字段,这些字段将显式表明此服务器是否允许这个跨域请求。

3.服务端 CORS

作为服务器开发人员,我们可以通过在 HTTP 响应中添加额外的响应头字段 Access-Control-* 来表明是否允许跨域请求。根据这些 CORS 响应头字段,浏览器可以允许一些被同源策略限制的跨源响应。

虽然有好几个 CORS 响应头字段[3],但有一个字段是必加的,那就是 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 的 Originhttps://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 方法。

在上图的案例中,只有GETPOSTPUT 方法被允许跨域访问资源。其他 HTTP 方法,例如 PATCHDELETE 都会被阻止。

如果您想知道其它的 CORS 响应头字段是什么以及它们的用途,可以查看此列表[4]

说到PUTPATCHDELETE 这几个 HTTP 方法,CORS 处理这些方法时还有些不同。这些非简单请求会触发 CORS 的预检请求。

4.预检请求

CORS 有两种类型的请求:一种是简单请求(simple request),一种是预检请求(preflight request)。一个跨域请求到底是简单的的还是预检的,取决于一些 request header。

当请求是 GETPOST 方法并且没有任何自定义 Header 字段时,一般来说就是个简单请求。除此之外的任何请求,诸如 PUTPATCHDELETE 方法,将会产生预检。

如果你想知道一个请求必须满足哪些要求才能成为简单请求,可以查看 MDN 简单请求相关的文档[5]

说了这么多,「预检请求」到底是什么意思?下面我们就来探讨一下。


1️⃣ 在发送实际请求之前,客户端会先使用 `OPTIONS`[6] 方法发起一个预检请求,预检请求的 Access-Control-Request-* 中包含有关我们将要处理的实际请求的信息:

  • 首部字段 `Access-Control-Request-Method`[7] 告知服务器,实际请求要用到的方法是什么

  • 首部字段 `Access-Control-Request-Headers`[8] 告知服务器,实际请求将附带的自定义请求首部字段是什么

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-Type

2️⃣ 服务器接收到预检请求后,会返回一个没有 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-Type

3️⃣ 浏览器收到预检响应,并检查是否应允许发送实际请求。

⚠️:上图预检响应漏了 Access-Control-Allow-Headers: Content-Type

4️⃣ 如果预检响应检测通过,浏览器会将实际请求发送到服务器,然后服务器返回我们需要的资源。

如果预检响应没有检验通过,CORS 会阻止跨域访问,实际的请求永远不会被发送。预检请求是一种很好的方式,可以防止我们访问或修改那些没有启用 CORS 策略的服务器上的资源。

???? 为了减少网络往返次数,我们可以通过在 CORS 请求中添加  Access-Control-Max-Age 头字段来缓存预检响应。浏览器可以使用缓存来代替发送新的预检请求。

5.认证

XHR 或 Fetch 与 CORS 的一个有趣的特性是,我们可以基于 Cookies[9] 和 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 文档[10]和 W3C 规范[11],这些一手知识是最准确的。

7.最后

这篇文章就到此结束了,如果觉得不错的话一定要点赞鼓励一下哦,祝大家学习进步,工作顺利!

❤️爱心三连击1.看到这里了就点个在看支持下吧,你的「在看」是我创作的动力。
2.关注公众号程序员成长指北,回复「1」加入Node进阶交流群!「在这里有好多 Node 开发者,会讨论 Node 知识,互相学习」!
3.也可添加微信【ikoala520】,一起成长。“在看转发”是最大的支持

15 张精美动图全面讲解 CORS相关推荐

  1. python编程思维导图_用来梳理 Python 编程核心知识15张思维导图

    原标题:用来梳理 Python 编程核心知识15张思维导图 小编这次在逛论坛的时候,无意中发现了一份python的武功秘籍,也就是一份思维导图,堪称业界经典! 思维导图可以有力地激发你的联想,通过一个 ...

  2. 美部长施压堵华为,遭印度电信巨头现场驳斥 ;WhatsApp被曝漏洞:仅凭一张GIF动图黑客便可接管账户……...

    关注并标星星CSDN云计算 极客头条:速递.最新.绝对有料.这里有企业新动.这里有业界要闻,打起十二分精神,紧跟fashion你可以的! 每周三次,打卡即read 更快.更全了解泛云圈精彩news g ...

  3. 『力荐汇总』这些 VS Code 快捷键太好用,忍不住录了这34张gif动图

    之前写过三篇文章,收获了极其不错的阅读量与转发量: 你真的会用 VS Code 的 Ctrl.Shift和Alt吗?高效易用的快捷键:多光标.跳转引用等轻松搞定 VS Code 中的 Vim 操作 | ...

  4. 如何合并多张GIF动图?教你一招多张GIF在线合成的方法

    多张图片合成GIF是我们常用的一种制作GIF动图的方法.那么,当我们手中有多张GIF动图想要合成一张GIF动图的时候应该怎么办呢?如何合并多张GIF呢?这时候,就可以使用gif在线制作(https:/ ...

  5. 学习php的15张思维导图

    15 张思维导图来快速学习 PHP 语言基础. PHP 简介 PHP安装 PHP语法 PHP数据类型 PHP变量 PHP数组(点击图片查看大图) PHP常量 PHP超级全局变量 PHP魔术变量 PHP ...

  6. 风靡全球的15则数学动图:让你秒懂数学概念,看后觉得相见恨晚啊

    数学抽象性和空间性非常大,对孩子的逻辑思维和发散思维有较高的要求,很多因素可能导致孩子们对数学产生了畏惧的心理,认为数学不管怎么学都没有提升了,导致很多同学干脆放弃了. 为了让孩子喜欢上数学,今天与各 ...

  7. 风靡全球的15则数学动图,让你秒懂数学概念

    1.被简单证明的勾股定理 给三角形加上一点厚度.从面积问题,跳转到了具象的体积问题. 2.勾股定理的面积证明法 It's a long story--慢慢看. 3.周长和直径的π点小事 4.圆的面积= ...

  8. 21张GIF动图让你秒懂数学原理

    全世界有3.14 % 的人已经关注了 数据与算法之美 数学是很难的科学,但因为它是科学家用数学来解释宇宙的语言,我们无可避免的要学习它.看看下面的这些GIF动图,它们提供了视觉的方式来帮助你理解各种数 ...

  9. 如何用一张影像动图来对比成都市2019年和2020年油菜花分布状态?

    春分至,燕子飞时,绿水人家绕.春雷细雨,万物皆醒,华夏大地处处生机盎然.此时的川蜀大地上油菜花盛开,鲜嫩欲滴的柔黄和生机勃勃的嫩绿相映交错,这样的美景尽收眼睑. 四川,仿佛上天遗留在人间的仙境,让人叹 ...

最新文章

  1. 自定义GridView分页模板
  2. 创新工场有哪些失败项目?不要只看着成功
  3. 共享思维导图,协作型思维导图Leangoo
  4. 分组卷积新进展,全自动学习的分组有哪些经典模型?
  5. Cython fatal error C1083: 无法打开包括文件: “numpy/arrayobject.h”: No such file or directory
  6. 【PC工具】更新速度最快最好用的文件内容搜索工具:searchmyfiles
  7. 员工培训案例分析答案_培训主管的技巧:培训教材问题解析、培训实施分析报告(附案例)...
  8. Science论文解读:打牌一时爽,一直打牌一直爽
  9. Codeforces Edu Round 64 A-D
  10. selenium架构_Selenium测试的干净架构
  11. 1005 继续(3n+1)猜想 (25分)
  12. 编程题练习 两个栈实现队列
  13. UI设计师样机模型|超酷折叠屏UI设计指南
  14. win10卸载电脑管家就蓝屏_新电脑WIN10出现蓝屏 系统重装也不行
  15. 网站维护不给提,问客服就说维护结束会给通知
  16. 不动产 - 权属性质代码
  17. 什么是bug?bug的分类
  18. 计算机硬件的五大逻辑部分,计算机的硬件系统由五大部分组成(计算机由几部分组成)...
  19. 阿拉伯数字翻译成中文的大写数字
  20. 12星座都是什么性格?(python爬虫+jieba分词+词云)

热门文章

  1. 【我的Android进阶之旅】Android调用JNI出错 java.lang.UnsatisfiedLinkError: No implementation found for的解决方法
  2. 如何下载丰台区卫星地图高清版大图
  3. 第二届全国大学生算法设计与编程挑战赛(冬季赛)题解
  4. webpack+ES6+less开发环境搭建(附带视频教程)
  5. AI+Science 黑客马拉松|赛程发布|10万奖金等你来拿!
  6. python 123 io网站答题如果最小化会有提示吗_爬虫进阶教程:百万英雄答题辅助系统...
  7. java二维数组的扩容_Java开发笔记(二十一)二维数组的扩展
  8. 国产Excel开发组件Spire.XLS【转换】教程(3):将 Excel 导出到 XML 和将 XML 导入到 Excel
  9. 【codevs3369】膜拜
  10. 新概念英语第二册课文电子版_新概念英语第二册音频+视频讲解:Lesson 24