写该系列文章的初衷是“让每位前端工程师掌握高频知识点,为工作助力”。这是前端百题斩的第27斩,希望朋友们关注公众号“执鸢者”,用知识武装自己的头脑。

27.1 简介

根据同源策略,浏览器默认是不允许XMLHttpRequest对象访问非同一站点的资源的,这会大大制约生产力,所以需要有一种机制允许跨域访问资源,然后我们的主角CORS(跨域资源共享)就出现了,该机制可以进行跨域访问控制,从而使跨域数据传输得以安全进行。

27.2 整体流程

CORS的通信流程是浏览器自动完成,不需要用户参与,其核心点是服务器,只要服务器实现了CORS接口就可以实现跨源通信了。虽然是浏览器自动完成,但是浏览器其实还是根据请求时字段的不同分为简单请求和非简单请求的,下面对这两者进行简要介绍。

27.2.1 简单请求

27.2.1.1 定义

只要满足以下两个条件就属于简单请求:

  1. 请求方法是一下三种方法之一:HEAD、GET、POST;

  2. HTTP的头信息不超出以下几种字段:Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type(其值为application/x-www-form-urlencoded、multipart/form-data、text/plain三个中的一个)。

27.2.1.2 流程

image-20210703182139849.png

简单请求的整个流程可以归结为以下步骤:

  1. 浏览器直接发出CORS请求,具体来说就是在头信息之中增加一个Origin字段,该字段用来说明请求来自哪个源(协议+域名+端口),服务器根据这个值决定是否同意这次请求;

  2. 当服务器接收到请求后,根据Origin判定指定的源是否在许可的范围内。

  3. 如果不在许可范围内,服务器会返回一个正常的HTTP回应,浏览器发现该回应的头信息没有包含Access-Control-Allow-Origin字段,就知道出错了,从而抛出错误,被XML的onerror回调函数捕获。(注意:由于正常回应,其状态码为200,所以该错误不能通过状态码识别)

  4. 如果Origin指定的域名在许可范围内,服务器返回的响应中会多出几个头信息字段(Access-Control-Allow-Origin、Access-Control-Allow-Credentials、Access-Control-Expose-Header等)。

27.2.1.3 关键字段
  1. Access-Control-Allow-Origin

必须字段,该值要么是请求的Origin字段值,要么是一个*(表示接受任意域名的请求)。

  1. Access-Control-Allow-Credentials

可选字段,其值是一个布尔值,表示是否允许发送Cookie。默认是不发送Cookie值,当设置为true时,表示服务器明确许可,Cookie可以包含在请求中发送给服务器。(注意:发送Cookie时要注意两点:一方面在Ajax请求中需要设置withCredentials属性;另一方面不能将Access-Control-Allow-Origin设置为*,需要指定明确的、与请求网页一致的域名)

  1. Access-Control-Expose-Header

可选字段,当CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段(Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma),如果想获取其它字段必须在Access-Control-Expose-Header中指定。

27.2.2 非简单请求

27.2.2.1 定义

不是简单请求的就是非简单请求,非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或Delete,或者Content-Type字段的类型是application/json.

27.2.2.2 流程

image-20210703211106355.png

非简单请求相比于简单请求较复杂,在发起正式请求之前会进行一次预检请求,通过预检请求的结果来决定是否进行后续的正式通信。

  1. 浏览器发起预检请求,该请求的请求方法是options,该请求是用来询问的;

  2. 服务器收到“预检”请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。

  3. 如果浏览器否定了“预检”请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段,这时浏览器就会认定服务器不同意预检请求,触发错误;

  4. 如果浏览器通过了“预检”请求,以后每次浏览器正常的CORS请求就跟简单请求一样,会有一个Origin头信息字段,服务器的回应也会有一个Access-Control-Allow-Origin头信息字段;

27.2.2.3 关键字段
  1. Access-Control-Request-Method

必须字段,用来列出浏览器的CORS请求会用到哪些HTTP方法。

  1. Access-Control-Request-Headers

该字段是一个逗号分隔的字符串,用来指定浏览器CORS请求会额外发送的头信息字段。

  1. Access-Control-Allow-Methods

必须字段,该值是一个逗号分隔的字符串,用来表明服务器支持的所有跨域请求的方法。

  1. Access-Control-Allow-Headers

该值是一个逗号分隔的字符串,表明服务器支持的所有头信息字段。

  1. Access-Control-Max-Age

用来请求预检请求的有效期,单位为秒。

27.3 实验

27.3.1 实验一

实验目的:

  1. 非同源会产生跨域问题

  2. 跨域是浏览器对响应拦截造成的

  1. 首先来看看浏览器控制台内容

控制台内容显示报错,报错内容是跨域,这是因为端口不同(一个8009一个8010),两者不同源,所以导致跨域,验证了实验目的1.

  1. 紧接着来瞅瞅服务器控制台打印了啥内容

控制台内容打印了接收到了get请求,则证明浏览器的请求发出了并被服务器端正常接收,从侧面证明了跨域是浏览器对响应进行了拦截,从而验证了实验目的2.

27.3.2 实验二

实验目的

  1. 服务器配置Access-Control-Allow-Origin会解决跨域问题

  2. 浏览器通过响应头中是否包含Access-Control-Allow-Origin这个响应头的值与请求头中Origin是否相等来确定是否能够进行跨域访问

  1. 首先来搂一眼请求头

  2. 紧接着再来搂一眼响应头

按照理论来说,请求头中的Origin字段表示本次请求来自哪个源(协议+域名+端口),服务器根据这个值来决定是否同意这次请求。如果Origin指定的源不在许可范围内,服务器会返回一个正常的HTTP回应。目前并没有修改,还处于报错状态,该响应确实是一个正常响应,不包含Access-Control-Allow-Origin字段,但是这也不足以说服我浏览器是通过验证该字段来确实是否允许跨域请求。所以紧接着需要做一个对比试验,通过修改服务端的代码后观察响应头内容。

  1. 从最简单的修改开始,直接将响应头中加入Access-Control-Allow-Origin=“http://127.0.0.1:8009” 字段,理论上来说此时会允许所有的跨域请求。

服务端代码修改后内容

app.get('/', (req, res) => {console.log('get请求收到了!!!');res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:8009');res.send('get请求已经被处理');
})

响应头内容

观察到响应头中多了一行内容:Access-Control-Allow-Origin:http://127.0.0.1:8009 字段,在看看响应内容,确实有消息返回了,内容如下:

  1. 只验证了Origin和Access-Control-Allow-Origin中内容响应,若内容不同又会有什么现象呢?

服务端代码进一步修改

app.get('/', (req, res) => {console.log('get请求收到了!!!');res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:8008');res.send('get请求已经被处理');
})

响应头内容

此时浏览器控制台报错了,出现了跨域问题

通过该实验可以验证通过配置Access-Control-Allow-Origin字段可以解决跨域问题;此外,浏览器是通过检查响应头中Access-Control-Allow-Origin字段的值与Origin的值是否相等来确定是否允许跨域访问的。通过该实验达到了我们实验的目的。

27.3.3 实验三

实验目的 验证CORS请求默认不发送Cookie信息,如果要把Cookie发送到服务器,一方面要服务器同意(通过指定Access-Control-Allow-Origin字段且Access-Control-Allow-Origin需要指定具体域名);另一方面浏览器请求中必须带上withCredentials字段。

  1. 通过观察请求头(看实验一中请求头),并不包含Cookie信息

  2. 代码修改

index.html页面进行修改

axios('http://127.0.0.1:8010', {method: 'get',withCredentials: true
}).then(console.log)

服务器端代码修改

app.get('/', (req, res) => {console.log('get请求收到了!!!');console.log('cookie 内容为', req.headers.cookie);res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:8009');res.setHeader('Access-Control-Allow-Credentials', true);res.cookie('test', 'test', {expires: new Date(Date.now() + 900000)});res.send('get请求已经被处理');
})
  1. 再次观察请求头内容,带上了cookie

  2. 看看服务器端有没有接收到cookie信息,控制台信息如下,确实收到了cookie信息

按照上述进行配置发送请求过程中将会带着cookie信息,上一个配置将会报错(可以自行验证)

27.3.4 实验四

实验目的

  1. 验证非简单请求会增加一次预检请求

  2. 预检请求是Options请求

  3. 请求头中会携带非简单请求的请求方法(Access-Control-Request-Methods)和头信息(Access-Control-Request-Headers),预检请求的响应头信息中Access-Control-Allow-Methods和Access-Control-Allow-Headers与上述请求头中的信息匹配才可以发送正常的CORS请求。

  1. 第一步肯定是要修改代码了

index.html代码

axios('http://127.0.0.1:8010', {method: 'post',headers: {'Content-Type': 'application/json'},data: {name: 'dog'}
}).then(console.log)

服务器代码修改如下

app.options('/', (req, res) => {console.log('options请求收到了!!!');res.setHeader('Access-Control-Allow-Origin', '*');res.setHeader('Access-Control-Allow-Headers', 'Content-Type');res.setHeader('Access-Control-Max-Age', 10000);res.send('options请求已经被处理');
});app.post('/', (req, res) => {console.log('post请求收到了!!!');res.setHeader('Access-Control-Allow-Origin', '*');res.send('post请求已经被处理');
});
  1. 修改完了代码是不是要瞅瞅结果呢?

先看看浏览器是不是输出了内容,确实有内容输出了

再瞅瞅服务器输出了些什么内容

可以看到本来打算发一次请求,但实际上发了两条,第一条是Options请求,第二条请求才是post请求,上述打印内容验证了实验目的中的一和二。

  1. 下面继续深入思考,来看看预检请求的请求头和响应头

请求头内容响应头内容

上述Access-Control-Request-Headers与Access-Control-Allow-Headers一样,而且内容也正常返回了(步骤二中已经进行了展示),但是这不足以证明实验目的三,下面我们认为增加一条头信息再来看结果。

  1. 人为增加一条请求头信息

index.html页面修改后如下

axios('http://127.0.0.1:8010', {method: 'post',headers: {'Content-Type': 'application/json','Test': 'test'},data: {name: 'dog'}
}).then(console.log)

此时浏览器控制台报错了

服务端只接收到了options请求

请求头信息为响应头信息为

通过该实验证明只有Access-Control-Request-Headers与Access-Control-Allow-Headers相等的时候,预检请求才会通过,后续请求才会发出,从而达到了该实验的实验目的三。

1.如果觉得这篇文章还不错,来个分享、点赞、在看三连吧,让更多的人也看到~

2.关注公众号执鸢者,领取学习资料,定期为你推送原创深度好文

3.关注公众号进群,里面大佬多多,一起向他们学习

1. 前端百题斩[001]——typeof和instanceof

2. 前端百题斩【002】——js中6种变量声明方式

3. 前端百题斩【003-004】——从基本类型、引用类型到包装对象

4. 前端百题斩【005】—— js中9种遍历对象的方法

5. 前端百题斩【006】——js中三类字符串转数字的方式

6. 前端百题斩【007】——js中必须知道的四种数据类型判断方法

7. 前端百题斩【008-009】——从JavaScript的代码执行过程到函数执行过程

8. 前端百题斩【010】——通俗易懂的JavaScript执行上下文

9. 前端百题斩【011】——通俗易懂的变量对象

10. 前端百题斩【012】——js中作用域及作用域链的真面目

11. 前端百题斩【013】——用“闭包”问题征服面试官

12. 前端百题斩【014】——js中的这些“this”指向都值得了解

13. 前端百题斩【015】——快速手撕call、apply、bind

14. 前端百题斩【016】——原型、构造函数和实例之间的奇妙关系

15. 前端百题斩【017】——一基础、二主线、双机制理解原型链

16. 前端百题斩【018】——从验证点到手撕new操作符

17. 前端百题斩【019】——数组中方法原理早知道

18. 前端百题斩【020】——竟然有五种方式实现flat方法

19. 前端百题斩【021】——通俗易懂的防抖与节流

20. 前端百题斩【022】——开拓思路之三种方式实现字符串转驼峰

21. 前端百题斩【023】——赋值、浅拷贝、深拷贝大PK

22. 前端百题斩【024】——我从浏览器控制台看到了五种存储方式

23. 前端百题斩【025】——原来跨域也是可以进行分类的

24. 前端百题斩【026】——浏览器出让安全性造就JSONP

25. 假如只剩下canvas标签

26. Vue源码思想在工作中的应用

27. 一文搞定Diff算法

28. 百度、小红书三面,均遇“赛马”问题

29. 十五张图带你彻底搞懂从URL到页面展示发生的故事

30. 一文搞懂Cookie、Storage、IndexedDB

前端百题斩【027】——解决跨域的常用利器CORS全解相关推荐

  1. 前端百题斩【032】——两个角度一个实战了解事件循环

    写该系列文章的初衷是"让每位前端工程师掌握高频知识点,为工作助力".这是前端百题斩的第32斩,希望朋友们关注公众号"执鸢者",用知识武装自己的头脑. 111 9 ...

  2. 前端百题斩【028】——浏览器中的请求们

    写该系列文章的初衷是"让每位前端工程师掌握高频知识点,为工作助力".这是前端百题斩的第28斩,希望朋友们关注公众号"执鸢者",用知识武装自己的头脑. 通过浏览器 ...

  3. 前端百题斩【035】——一文了解HTTP缓存

    写该系列文章的初衷是"让每位前端工程师掌握高频知识点,为工作助力".这是前端百题斩的第35斩,希望朋友们关注公众号"执鸢者",用知识武装自己的头脑.提前透露一下 ...

  4. 前端百题斩【031】——从渲染流程认识重绘和回流

    写该系列文章的初衷是"让每位前端工程师掌握高频知识点,为工作助力".这是前端百题斩的第31斩,希望朋友们关注公众号"执鸢者",用知识武装自己的头脑. 在&quo ...

  5. 前端百题斩【029】——原来浏览器中存在五类进程

    写该系列文章的初衷是"让每位前端工程师掌握高频知识点,为工作助力".这是前端百题斩的第29斩,希望朋友们关注公众号"执鸢者",用知识武装自己的头脑. 浏览器已经 ...

  6. 前端百题斩【030】——神奇的浏览器渲染流程

    写该系列文章的初衷是"让每位前端工程师掌握高频知识点,为工作助力".这是前端百题斩的第30斩,希望朋友们关注公众号"执鸢者",用知识武装自己的头脑. 本篇文章是 ...

  7. 前端百题斩【025】——原来跨域也是可以进行分类的

    写该系列文章的初衷是"让每位前端工程师掌握高频知识点,为工作助力".这是前端百题斩的第25斩,希望朋友们关注公众号"执鸢者",用知识武装自己的头脑. 25.1 ...

  8. 五万字前端面试宝典——前端百题斩(上)新鲜出炉

    从2021年5月9号开始更新<前端百题斩>,目前终于取得了阶段性成果,<前端百题斩>上册已经更新完毕,内容包括JS基础篇.浏览器篇.网络篇,共计50个章节,5万多字.关注公众号 ...

  9. 前端百题斩【024】——我从浏览器控制台看到了五种存储方式

    写该系列文章的初衷是"让每位前端工程师掌握高频知识点,为工作助力".这是前端百题斩的第24斩,希望朋友们关注公众号"执鸢者",用知识武装自己的头脑. 打开浏览器 ...

最新文章

  1. 虚拟机VMWare“提示:软件虚拟化与此平台上的长模式不兼容”的解决方法
  2. 移动端自动化测试(一)appium环境搭建
  3. VScode使用python的yapf库
  4. 自定义 Spring Boot Starter
  5. 什么是 DMZ 区?
  6. 无后端完成在线翻译功能
  7. 解决QTreeWidget中item无法整行同时显示相同颜色
  8. 从Sun离职后,我“抛弃”了Java,拥抱JavaScript和Node
  9. 10分钟带你学会微信小程序的反编译
  10. 【Java】Java 8 新特性-----Lambda 表达式
  11. 大学使用python 编辑器_Python数据分析|最多人用的代码编辑器推荐
  12. 安装一级计算机失败,Win7/Win8.1升级Win10提示“安装失败”的解决方法
  13. OpenCV图像处理(下) 边缘检测+模板匹配+霍夫变换
  14. 华大单片机HC32L130使用内部RCH时钟源倍频24M外设PCLK到48M
  15. 【python】pythonPTA编程练习2
  16. ssd hdd linux分区方案,windows10+ubuntu 16.04+双硬盘(SSD+HDD)分区(图文)
  17. 不要把学习技术当做任务、攀比和终极目标。 [IT傻博士原创]
  18. CNN卷积神经网络及图像识别
  19. 系统清理软件MacBooster 7破解版
  20. PID温控实验平台搭建(二)——PID进阶知识介绍及源码分享

热门文章

  1. Android中的内存泄漏和内存溢出
  2. hihocoder 1569 [Offer收割]编程练习赛25 : 无限巧克力谜题
  3. java mysql判断字符串相等_java如何判断字符串是否相等?
  4. ChatGPT的字数限制是什么?如果解决字数限制
  5. 解决esxi主机vmware 无法清除磁盘的报错
  6. 《单片机》实验——实验3 MCS-51内部定时/计数器实验(1)
  7. 解决Mysqlf服务启动后停止。某些服务器在未由其他服务或程序使用时将自动停止的问题
  8. 如何学习ui设计-庞姿姿
  9. libvirt/qemu外置快照命令
  10. 等保级别最高为几级?市面上常见吗?