透彻理解跨域资源共享(CORS)

news2024/9/24 0:30:19

1. 什么是源?

源(Origin)

概念

跨域资源共享英文全称为"Cross-Origin Resource sharing",在介绍它之前先要搞懂其中的"Origin"是什么意思。

Origin译为源,指的是网页内容的来源,我们通过访问某个URL获取到文字、图片等资源,这个URL就是获取这些资源的源。

Origin实际上由三部分组成:

  • schema(协议): 如http/https
  • hostname(域名/地址): www.baidu.com
  • port(端口): 如80/443

只有协议、主机名/域名、端口完全一致,我们才称之为同源。

示例

下面两个同源,因为它们有相同的协议(http)、主机名(example.com)、端口(并没有指定端口但默认都是80端口)。具体的文件路径不同,这不影响同源:

  • http://example.com/app1/index.html
  • http://example.com/app2/index.html

下面两个同源(默认通过80端口访问):

  • http://example.com:80
  • http://example.com

下面两个不同源,因为端口不同:

  • http://example.com
  • http://example.com:8080

下面几个不同源,因为域名不同:

  • http://example.com
  • http://example.org
  • http://www.example.com
  • http://myapp.example.com

2. 什么是跨源?

跨源(Cross-Origin)

“Cross-Origin"常被翻译为"跨域”,这样翻译有歧义,因为域、域名、源是不同的名词,互相之间还存在关系:比如域名(domain)就是源(origin)的组成部分之一,因此我更倾向于翻译为跨源而不是跨域。

最常见的跨源场景是前后端分离的项目:

  • 用户访问http//frontend.com从前端服务器请求如html、js等静态资源。
  • 用户获取到的js脚本中可能包含fetch('http//backend.com')用于向后端服务器请求数据。
  • 用户点击页面中的某个按钮,触发js代码,请求开始发送。
  • 用户本来是从http//frontend.com获取资源,现在又要向http//backend.com发送请求,这个行为就是跨源请求行为。

3. 什么是同源策略?

同源策略(Same-origin policy)

同源策略是浏览器的一种安全策略,它限制了脚本从不同的源进行资源加载,这助于隔离潜在的危险,减少可能的攻击途径。例如,互联网上的恶意网站在用户的浏览器中运行JavaScript脚本向其它服务器发送请求。

你可以尝试随便打开一个不属于https://www.csdn.net/的网站,在控制台执行fetch('https://www.csdn.net/'),你会看到下面的错误:

Access to fetch at 'https://www.csdn.net/' from origin 'https://cn.vitejs.dev' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

对于前后端不分离的项目来说,静态资源和数据库中的数据都是来自同一台服务器,基本不存在跨源行为。而对于前后端分离的项目来说,静态资源在前端服务器,业务数据在后端服务器,所以一定会存在跨源请求,然而浏览器会限制这种行为,只是我们需要解决的问题。

4. 什么是跨域资源共享?

跨域资源共享(Cross-Origin Resource sharing)

下面翻译为"跨源资源共享"

概念

为了跨源,我们需要使用跨源资源共享(CORS),CORS是HTTP协议的一部分,是一种基于HTTP请求头的机制,它允许服务器指明哪些自身之外的源可以来请求它的数据。

跨源资源共享的工作原理就是添加一条HTTP请求头,让服务器明确告诉浏览器客户端哪些源可以访问它。此外那些可能对服务器数据产生副作用的HTTP请求方法(比如UPDATE),规范还要求浏览器向服务发送一个预检请求(使用OPTIONS方法),该请求将在正式发送请求之前,向服务器查询支不支持即将到来的请求方法,得到服务器明确的"批准"之后,再发送实际的内容。

简单请求的定义

不是所有请求都会触发上面所述的预检请求。不会触发预检请求的方法,我们一般称之为简单请求,简单请求要满足下面的条件:

  • 只能使用如下方法:
    • GET
    • HEAD
    • POST
  • 自定义的请求头(除了自动设置的请求头)只能包含:
    • Accept
    • Accept-Language
    • Content-Type(还有额外要求)
    • Range
  • Content-Type只能设置为:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

不满足上述条件就不是简单请求,就会触发预检机制,也就是在正式请求之前使用OPTIONS方法发送一条预检请求。

具体示例

  • 简单请求

用户浏览器客户端通过前端服务器http//frontend.com获取到JavaScript脚本,该脚本向后端服务器http//backend.com使用GET方法请求Json数据:

const fetchPromise = fetch("http//backend.com");

fetchPromise
  .then((response) => response.json())
  .then((data) => {
    console.log(data);
  });

具体发送的HTTP请求内容如下:

GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: http//frontend.com

此时可以看到Origin请求头被浏览器自动设置为http//frontend.com,我们看看后端如何响应:

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml

[…XML Data…]

后端返回响应,响应头中包含了一条Access-Control-Allow-Origin: *,这表示后端告诉浏览器,所以的源都可以访问我。

通过OriginAccess-Control-Allow-Origin标头是实现跨域资源访问最简单和标准的做法。这里如果后端只希望http//frontend.com这一个源可以访问它,它可以设置响应头为Access-Control-Allow-Origin: http//frontend.com

  • 预检请求

与简单请求不同,对于预检请求来说,浏览器首先会使用OPTIONS方法向后端(另一个源发)发送一个HTTP请求,以确认接下来的实际请求是否可以安全发送。

js脚本:

const fetchPromise = fetch("http//backend.com/doc", {
  method: "POST",
  mode: "cors",
  headers: {
    "Content-Type": "text/xml",
    "X-PINGOTHER": "pingpong",
  },
  body: "<person><name>Arun</name></person>",
});

fetchPromise.then((response) => {
  console.log(response.status);
});

注意请求中设置了一个非标准请求头X-PINGOTHER,因此这不是一个简单请求。

当请求发送,浏览器客户端和后端服务器之间进行第一次交互:

OPTIONS /doc HTTP/1.1
Host: frontend.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: https://frontend.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type,x-pingother

HTTP/1.1 204 No Content
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2
Access-Control-Allow-Origin: frontend.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive

观察浏览器客户端发送的OPTIONS请求的请求头,除了Origin之外,还有如下两个字段:

Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type,x-pingother

相当于浏览器客户端问后端服务器:

  • 你允许使用POST方法的请求吗?
  • 你允许请求头中包含content-type,x-pingother吗?

观察后端服务器响应头中如下几个字段:

Access-Control-Allow-Origin: https://frontend.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400

相当于服务器回答说:

  • 我允许https://frontend.com这个源访问我
  • 我允许POST,GET,OPTIONS这三种请求方式
  • 我允许请求头中包含X-PINGOTHER, Content-Type
  • 在之后86400秒也就是一天的时间中,此类请求无需再发送OPTIONS预检了。

整个预检的流程走完,才是真正的请求:

POST /doc HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Referer: https://frontend.com/examples/preflightInvocation.html
Content-Length: 55
Origin: frontend.com
Pragma: no-cache
Cache-Control: no-cache

<person><name>Arun</name></person>

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2
Access-Control-Allow-Origin: https://frontend.com
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain

[Some XML content]

总结

我们常说解决跨域问题,本质就是在请求的过程中加上一些请求头。前端的Origin请求头是自动的,因此解决跨域主要是后端的工作。对于常见的后端框架来说只需要通过中间件,或者在有些框架中称之为依赖的东西,为响应自动加上CORS所需要的响应头,告诉客户端浏览器我允许的源、方法、请求头等信息,这也有利于保证我们的后端安全。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2158886.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

前端-js例子:todolist

实现效果图&#xff1a; 实现步骤&#xff1a; 1.body部分 1.首先&#xff0c;设置输入数据的框和按钮进行操作 2.设置一个表格标签&#xff08;有边框&#xff09;&#xff0c;首先在表头放置两列&#xff08;“事项”‘’操作&#xff09; <body><div class"…

初学者怎么入门大语言模型(LLM)?看完这篇你就懂了!

当前2024年&#xff0c;LLM领域发展日新月异&#xff0c;很多新的实用技术层出不穷&#xff0c;个人认为要跟上LLM的发展&#xff0c;需要掌握以下内容&#xff0c;并需要不断地跟踪学习。 入门LLM前置基础 深度学习基础知识&#xff1a;推荐李宏毅的深度学习课程Python和num…

数据结构2——单链表

目录 1.链表 1.1链表的概念及结构 1.2 链表的分类 ​编辑2.无头单链表的实现 1. 节点 2.遍历链表 3.动态增加新节点 4.查找&#xff08;修改&#xff09; 5.插入 5.1 尾插 5.2 头插 5.3 在pos之前插入x 5.4 在pos之后插入x 6.删除 6.1 尾删 6.2 头删 6.3 删除…

DPDK 简易应用开发之路 4:基于Pipeline模型的DNS服务器

本机环境为 Ubuntu20.04 &#xff0c;dpdk-stable-20.11.10 使用scapy和wireshark发包抓包分析结果 完整代码见&#xff1a;github Pipeline模型 DPDK Pipeline模型是基于Data Plane Development Kit&#xff08;DPDK&#xff09;的高性能数据包处理框架。它通过将数据流分为多…

基于SpringBoot+Vue+MySQL的旅游推荐管理系统

系统展示 用户前台界面 管理员后台界面 系统背景 随着社会的快速发展和人民生活水平的显著提高&#xff0c;旅游已成为人们休闲娱乐的重要方式。然而&#xff0c;面对海量的旅游信息和多样化的旅游需求&#xff0c;如何高效地管理和推荐旅游资源成为了一个亟待解决的问题。因此…

学习记录:js算法(四十三):翻转二叉树

文章目录 翻转二叉树我的思路网上思路递归栈 总结 翻转二叉树 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点 图一&#xff1a; 图二&#xff1a; 示例 1&#xff1a;&#xff08;如图一&#xff09; 输入&#xff1a;root [4,2,7,1…

大模型价格战,打到了负毛利,卷or不卷?

国产大模型淘汰赛在加速。这轮淘汰赛会持续一两年&#xff0c;只有少数真正具备实力的基础模型企业能继续活下去 中国市场的大模型价格战已经打了近半年。这轮价格战已经打到了负毛利&#xff0c;而且暂时没有停止迹象。头部云厂商仍在酝酿新一轮降价。这轮降价会在今年9月下旬…

视频单目标跟踪研究

由于对视频单目标跟踪并不是很熟悉&#xff0c;所以首先得对该领域有个大致的了解。 视频目标跟踪是计算机视觉领域重要的基础性研究问题之一&#xff0c;是指在视频序列第一帧指定目标 后&#xff0c;在后续帧持续跟踪目标&#xff0c;即利用边界框&#xff08;通常用矩形框表…

printf 命令:格式化输出

一、命令简介 ​printf​ 命令在 Linux 系统中用于格式化并打印字符串到标准输出。它是 C 语言中 printf ​函数的命令行版本&#xff0c;因此其格式化选项与 C 语言中的非常相似。 相关命令&#xff1a; echo&#xff1a;通常使用 echo&#xff0c;它比较简单。printf&…

你们用过微信CRM管理系统吗?

微信CRM管理系统是近年来流行的管理软件&#xff0c;在市场上得到了很高的认可。许多企业正在应用微信CRM管理系统&#xff0c;那系统具体有些什么功能呢&#xff1f; 1、聚合聊天&#xff0c;可以管理多个微信号 2、批量多号自动加好友任务&#xff0c;设置好时间间隔以及加人…

《论软件系统架构风格》写作框架,软考高级系统架构设计师

论文真题 系统架构风格&#xff08;System Architecture Style&#xff09;是描述某一特定应用领域中系统组织方式的惯用模式。架构风格定义了一个词汇表和一组约束&#xff0c;词汇表中包含一些构件和连接件类型&#xff0c;而这组约束指出系统是如何将这些构件和连接件组合起…

李沐对大模型趋势的几点判断,小模型爆发了!

李沐是上海交通大学 2011 届计算机科学与工程系本硕系友。他曾担任亚马逊资深首席科学家&#xff0c;加州大学伯克利分校和斯坦福大学的访问助理教授&#xff0c;是前 Marianas Labs 联合创始人&#xff0c;深度学习框架 Apache MXNet 的创始人之一。目前是 BosonAI 联合创始人…

C++之STL—string容器

本质&#xff1a;类 class 封装了很多方法&#xff1a;查找find&#xff0c;拷贝copy&#xff0c;删除delete 替换replace&#xff0c;插入insert 构造函数 赋值操作 assign&#xff1a; 字符串拼接 &#xff0b; append&#xff1a; string查找和替换 没查找到&#xff0c;po…

【刷题2—滑动窗口】最大连续1的个数lll、将x减到0的最小操作数

目录 一、最大连续1的个数lll二、将x减到0的最小操作数 一、最大连续1的个数lll 题目&#xff1a; 思路&#xff1a; 问题转换为&#xff1a;找到一个最长子数组&#xff0c;这个数组里面0的个数不能超过k个 定义一个变量count&#xff0c;来记录0的个数&#xff0c;进窗口、…

时序预测 | Python实现KAN+LSTM时间序列预测

时序预测 | Python实现KAN+LSTM时间序列预测 目录 时序预测 | Python实现KAN+LSTM时间序列预测预测效果基本介绍程序设计预测效果 基本介绍 时序预测 | KAN+LSTM时间序列预测(Python) KAN作为这两年最新提出的机制,目前很少人用,很适合作为时间序列预测的创新点,可以结合…

【重学 MySQL】三十八、group by的使用

【重学 MySQL】三十八、group by的使用 基本语法示例示例 1: 计算每个部门的员工数示例 2: 计算每个部门的平均工资示例 3: 结合 WHERE 子句 WITH ROLLUP基本用法示例注意事项 注意事项 GROUP BY 是 SQL 中一个非常重要的子句&#xff0c;它通常与聚合函数&#xff08;如 COUNT…

MySQL和SQL的区别简单了解和分析使用以及个人总结

MySQL的基本了解 运行环境&#xff0c;这是一种后台运行的服务&#xff0c;想要使用必须打开后台服务&#xff0c;这个后台服务启动的名字是在安装中定义的如下图&#xff08;个人定义MySQL88&#xff09;区分大小写图片来源 可以使用命令net start/stop 服务名&#xff0c;开…

实验十八:IIC-EEPROM实验

这个实验比较复杂,是目前第一个多文件项目 KEY1-4:P3^0-P3^3 IIC_SCL=P2^1; IIC_SDA=P2^0; //定义数码管位选信号控制脚 LSA=P2^2; LSB=P2^3; LSC=P2^4; 代码 main.c #include "public.h" #in

常见汽车零部件ASIL等级示例

ASIL&#xff08;Automotive Safety Integrity Level&#xff0c;汽车安全完整性等级&#xff09;评级系统是ISO 26262标准中定义的一套风险分类体系&#xff0c;用于评估道路车辆中电子电气系统&#xff08;E/E系统&#xff09;功能安全的风险程度&#xff0c;并确保这些系统在…

Linux相关概念和重要知识点(6)(make、makefile、gdb)

1.make、makefile &#xff08;1&#xff09;什么是make、makefile&#xff1f; 在我们写完代码后&#xff0c;要编译运行&#xff0c;如果有多个.c文件就需要每次都自己用gcc -o来处理&#xff0c;这十分麻烦。当我们想要自定义多个文件的处理时&#xff0c;我们会浪费很多时…