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

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-pingotherHTTP/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.xdnf.cn/news/148910.html

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

相关文章

常见的并发集合(java.util.concurrent)

1.CopyOnWriteArrayList 1.1 并发修改时保证线程安全: 通过ReentrantLock实现多个线程并发修改时的线程安全同步&#xff08;添加元素的同时&#xff0c;不允许删除&#xff09;添加新元素&#xff1a;list.add("")按照指定下标替换元素&#xff1a;list.set(index…

【Python调用ddddocr打包成exe文件指定模型库及注意事项】

ddddocr 打包成 exe 后一直存在各种各样的问题&#xff0c;例如&#xff1a; ddddocr\common.onnx failed. File doesn’t exist 查阅资料后&#xff0c;问题得到解决。但相关资料不多&#xff0c;且不够详细&#xff0c;特写下本文&#xff0c;以便于后来者解决问题。 希望本文…

java -----泛型

泛型的理解和好处 泛型是在JDK5之后引入的一个新特性&#xff0c;可以在编译阶段约束操作的数据类型&#xff0c;并进行检查。 泛型的格式为 <数据类型> import java.util.ArrayList;SuppressWarnings({"all"}) public class Generic02 {public static void…

【iOS】MVC架构模式

文章目录 前言MVC架构模式基本概念通信方式简单应用 总结 前言 “MVC”&#xff0c;即Model&#xff08;模型&#xff09;&#xff0c;View&#xff08;视图&#xff09;&#xff0c;Controller&#xff08;控制器&#xff09;,MVC模式是架构模式的一种。 关于“架构模式”&a…

【有奖问答 / B】

题目 DP代码 #include <bits/stdc.h> using namespace std; int f[35][100]; int sum; int main() {f[0][0] 1;for (int i 1; i < 30; i){for (int j 0; j < 90; j){f[i][0] f[i - 1][j];if (j 10 < 90)f[i][j 10] f[i - 1][j];}sum f[i][70];}cout &l…

动手学深度学习9.1. 门控循环单元(GRU)-笔记练习(PyTorch)

本节课程地址&#xff1a;门控循环单元&#xff08;GRU&#xff09;_哔哩哔哩_bilibili 本节教材地址&#xff1a;9.1. 门控循环单元&#xff08;GRU&#xff09; — 动手学深度学习 2.0.0 documentation (d2l.ai) 本节开源代码&#xff1a;...>d2l-zh>pytorch>chap…

信息学奥赛复赛复习01-CSP-J2019-01-字符、字符数组、字符串、string、字符串读取

PDF文档回复:20240923 1 2019 CSP-J 题目1 数字游戏 [题目描述] 小 K 同学向小 P 同学发送了一个长度为 8 的 01 字符串来玩数字游戏&#xff0c;小 P 同学想要知道字符串中究竟有多少个 1。 注意&#xff1a;01 字符串为每一个字符是 0 或者 1 的字符串&#xff0c;如 101 …

文心智能体搭建步骤

通过使用文心智能体平台来创建智能体的过程。这种方法可以让没有编程经验的人也能快速构建智能体&#xff0c;降低了技 术门槛。以下是一些建议和心得: 1.选择合适的平台:文心智能体平台是一个优秀的选择&#xff0c;它提供了零代码和低代码的开发环境&#xff0c;极大地降低了…

vue中高德地图使用 Marker 标点 - 标点数据快到 1000 时页面卡顿问题解决(已解决 - 多方面原因)+ 海量点功能实现解决

目录 1.业务需求2.最初实现及出现的问题3.解决 - 1000 个标点时页面就出现 卡顿 问题4.使用海量点、聚合标点后还有卡顿&#xff0c;排查其他原因5.最终解决5.1页面中list数据渲染问题解决5.2地图相关实例不要放在 vue 的可响应数据中 页面展示 1.业务需求 需要在 高德地图 中标…

【linux驱动】什么是驱动

内核简介 在谈驱动之前&#xff0c;我们需要先谈内核。 内核&#xff0c;是一个操作系统的核心。是基于硬件的第一层软件扩充&#xff0c;提供操作系统的最基本的功能&#xff0c;是操作系统工作的基础&#xff0c;决定着整个操作系统的性能和稳定性。 下面是微内核和宏内核…

oracle avg、count、max、min、sum、having、any、all、nvl的用法

组函数 having的使用 any的使用 all的使用 nvl 从执行结果来看&#xff0c;nvl(列名&#xff0c;默认值)&#xff0c;nvl的作用就是如果列名所在的这一行出现空则用默认值替换

Offer60:n个骰子的点数

题目&#xff1a;把n个骰子扔在地上&#xff0c;所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。 分析:要解决这个问题&#xff0c;我们需要先统计出每个点数出现的次数&#xff0c;然后把每个点数出现的次数除以,就能求出每个点数出现的概率了。我们…

day-58 最接近的三数之和

思路 双指针&#xff1a;首先利用Arrays.sort()函数对数组进行排序&#xff0c;通过枚举中间位置的那个数&#xff0c;题目就变为了双指针 解题过程 指针边界&#xff08;right>i&&left<i&#xff09;&#xff0c;当三数之和小于等于target时&#xff0c;left,反…

电力系统中有哪些好的运维的平台?

摘要&#xff1a;介绍台商大厦&#xff0c;采用综合保护装置、多功能仪表、变压器温控仪、直流屏、烟雾传感器、门磁开关、网络摄像头等设备&#xff0c;采集配电现场的各种电参量和状态信号。系统采用现场就地组网的方式&#xff0c;组网后通过现场网关远传至阿里云平台&#…

精神分裂症、抑郁症和异常衰老的脑电信号特征:一项监督式机器学习研究

摘要 背景&#xff1a;EEG是一种非侵入性、成本效益高且稳健的工具&#xff0c;能够以高时间分辨率直接测量体内神经元的整体活动。结合先进的机器学习(ML)技术&#xff0c;EEG记录可以提供有关精神疾病的计算机模拟生物标志物。 假设&#xff1a;病理性和生理性衰老过程会影…

CMake 构建Qt程序弹出黑色控制台

CMake 构建Qt程序弹出黑色控制台

OpenBayes 一周速览|IC-Light 图片打光神器一键启动!Tecnalia 电子设备废物高光谱数据集上线,提高电子废物回收准确性

公共资源速递 This Weekly Snapshots &#xff01; 5 个数据集&#xff1a; * BTAD 工业异常数据集 * WebVid 大型短视频数据集 * bAbi 问答和文本理解的数据集 * OpenMIR 音乐收听脑电图数据集 * Tecnalia 电子设备废物高光谱数据集 2 个教程&#xff1a; * ComfyUl …

随机掉落的项目足迹:Vue3 + wangEditor5富文本编辑器——toolbar.getConfig() 查看工具栏的默认配置

问题引入 小提示&#xff1a;问题引入是一个讲故事的废话环节&#xff0c;各位小伙伴可以直接跳到第二大点&#xff1a;问题解决 我的项目不需要在富文本编辑器中引入添加代码块的功能&#xff0c;于是我寻思在工具栏上把操作代码的菜单删一删 于是我来到官网文档工具栏配置 …

vue-baidu-map的基本使用

前言 公司项目需求引入百度地图&#xff0c;由于给的时间比较短&#xff0c;所以就用了已经封装好了的vue-baidu-map 一、vue-baidu-map是什么&#xff1f; vue-baidu-map是基于vue.js封装的百度地图组件(官方文档) 二、使用步骤 1.下载插件 //我下载的版本 npm install …