谈谈对跨域(跨源)的一些理解

news2024/12/22 14:42:37

一、相关概念

1、什么是跨域?

跨域又称为跨源,是指在违反了浏览器的同源政策,也就是协议、域名和端口号三者不完全一致的情况下产生的。只要客户端与浏览器的三者有一项不同,就属于不同源,就会产生跨域。
很多初级开发者有一种错误的认知:只有ajax请求才会产生跨域。但其实这是一种很片面的想法,因为浏览器的同源政策不仅限制了ajax请求,同时还限制了图片、字体、css、音视频文件、iframe等其他诸多互联网资源,只是各自被限制后的行为表现有所不同而已。

2、什么是跨站?

与跨域类似的,还有一个跨站的概念,跨站仅仅是指域名不同的情况(包括顶级域名和二级域名),所以它与跨域其实是一种包含关系:跨站一定会跨域,但跨域不一定会跨站。
例如:从f1.a.com中向f2.a.com发起ajax请求会构成跨域,但不构成跨站,因为两者的域名相同;而从f1.a.com向f1.b.com发出的请求构成跨站,同时也是跨域,因为两者的二级域名不同。

3、什么是域名?

域名是互联网上某一台计算机的名称,由一串通过.进行连接的字符串组成,用于在进行数据传输时标识对应的计算机的电子方位。而且域名其实是IP地址的一种代称,当我们使用域名去访问某个网站时,互联网会通过DNS 去查找对应的IP地址,然后才能访问到对应的计算机。
域名本身具有一套很复杂的命名规则,在此我们就不过多赘述了,其格式大约为[三级域名].二级域名.顶级域名,我们以www.three.two.com为例,进行简单了解,其中.com是顶级域名,.two是二级域名,.three是三级域名,而www则是主机名。

二、相关理解

1、跨域存在的意义

我们讲了这么多的跨域的解决方案,但我们不得不思考这样一个问题:浏览器为什么要设置这么一道障碍?几乎所有人都知道答案,那就是为了安全。可这个安全是为了谁?
有人说是为了浏览器的安全,避免恶意脚本的执行,对客户端造成损害,但这是片面的。
也有人说是为了服务端的安全,避免服务器被攻击,但通过抓包可以发现,当出现跨域时,其实服务端的数据已经完整返回了,也就是说其实服务器对于跨域并不知情,所以这种说法也是片面的。
其实跨域真正的意义是为了保护数据资源的安全,例如:图片、字体、音视频、css、js等等。也就是说:我们可以通过跨域,去决定我们的私有资源对于哪些应用是开放的,对于那些资源是禁止访问的。
以生活中的例子来说:资源是超市货架上的物品,任何用户都可以进入超市,是不被拦截的。但是出门的时候,如果你没有付款,商品是会被拦截的。收银员这时候就相当于浏览器的角色,对商品和用户进行校验,只有用户付款之后,有了权限,才能让用户拿着商品离开,否则人离开,商品留下,也就是产生跨域了。

2、跨域的几种解决方案

请转看我的这一篇博客:同源政策和跨域解决方案

3、当请求跨域时,怎样才能携带相关凭据?

ajax请求发生跨域时,就算通过上面的某种方式解决了跨域,但是该请求默认还是不会携带相关凭据的(如:cookie、SSL证书、HTTP认证信息等)。但某些场景下我们是需要这些凭据的。
想要解决这个问题我们需要客户端和服务端共同配合,客户端需要设置xhr.withCredentialstrue,此时如果是跨域,则会携带所有cookie,如果是跨站,则只会携带samesite=none且设置了Secure属性的cookie(同时还必须是https协议,否则不会生效)。


xhr.withCredentials = true;

Set-Cookie: ***** SameSite=None; Secure

客户端除了代码层面上的设置,还需要检查浏览器的设置,如果浏览器设置了“组织第三方cookie”选项,那么跨域时也无法携带任何cookie(Safari任意模式和Chrome的无痕模式下默认勾选,Chrome正常模式下未勾选)
在这里插入图片描述

服务端则需要设置响应头中的Access-Control-Allow-Credentialstrue

// 以nginx为例
add_header Access-Control-Allow-Credentials true;

4、简单请求产生跨域的具体方案

首先我们要搞清一个概念:什么是简单请求?
当一个请求同时满足下面五个条件时,就是一个简单请求:

  • ① 请求方式为 get/head/post 三者之一。
  • ② 除了user-agent自动设置的头部之外,仅能使用cors安全的头部(请求头)之一:acceptaccept-languagecontent-languagecontent-type
  • content-type只能是:text/plainmultipart/form-dataapplication/x-www-form-urlencoded其中之一。
  • ④ 请求中的任意 XMLHttpRequest 对象均没有注册任何事件监听器;XMLHttpRequest 对象可以使用 XMLHttpRequest.upload 属性访问。
  • ⑤ 请求中没有使用 ReadableStream 对象。

通常情况下,条件④、⑤不会出现,所以我们主要关注前三条即可。
对于简单请求产生跨域时,我们只需要在服务端设置响应头:Access-Control-Allow-Origin: *(或者具体的origin地址),即可解决跨域问题。
但是当客户端设置请求的xhr.withCredentials = true时,请求会携带凭据,所以Access-Control-Allow-Origin的值就不能设置为 *,必须为具体的origin地址。

//这里可以是变量,也可以是具体写固定的origin
add_header Access-Control-Allow-Origin $http_origin;

5、复杂请求解决跨域的具体方案

知道了简单请求的定义,所以只要不是简单请求,那就一定是复杂请求。常见的几种情况:

  • ① 请求方法为PUTDELETE等除了HEADGETPOST之外方法。
  • ② 请求头中的Content-Type值为application/json
  • ③请求头中有自定义头部,比如我们鉴权经常会使用的“Auth”字段,会常放在请求头中。

复杂请求进行时,分为两步:第一步是客户端发出:options请求(预检请求,HTTP/1.1),该请求将携带以下的请求头部信息:

// 告诉服务器,接下来将使用什么请求方法
Access-Control-Request-Method 
// 告诉服务器接下来将携带哪些请求头部(除了user-agent预设的头部之外,通常是自定义头部)
Access-Control-Request-Headers 

服务端接收到预检请求之后,会对其进行校验,然后返回以下响应头部信息:

// 告诉客户端允许的请求方法
Access-Control-Allow-Method  
// 告诉客户端允许携带的头部(如果有自定义头部,需要在这里列出)
Access-Control-Allow-Headers 
// 预检请求的响应结果(Access-Control-Allow-Method和Access-Control-Allow-Headers)的缓存时间,不能超过浏览器的默认上限(当前版本Chrome为2小时,firfox24小时)。-1为禁用缓存。
Access-Control-Allow-Max-Age 
// 告知哪些头部可以供客户端通过xhr.getResponseHeader获取到,逗号分隔。即使不设置,也不影响跨域请求的完成
Access-Control-Expose-Headers
 // 告诉客户端允许的origin,对于复杂请求,不能为通配符*,只能为具体的origin 
Access-Control-Allow-Origin 
/ 告诉客户端是否允许携带凭据(cookie)
Access-Control-Allow-Credentials 

以上的响应头部信息,并不是必须全部返回的,我们可以根据具体情况决定返回那些头部信息:

  • ① 只有Access-Control-Allow-Origin是必须返回的。
  • ② 如果没有特殊请求头,可以不返回Access-Control-Allow-Headers
  • ③ 如果没有携带凭据,可以不返回Access-Control-Allow-Credentials

特殊的,当客户端请求携带凭据(withCredentials=true)时,以下三个响应头部的值不能设置为通配符*

//当XHR请求设置了withCredentials=true,以下头部的值不能为*,需要设置具体的值
// 必须为具体的origin,不然会报错提示不能为*
Access-Control-Allow-Origin 
// 必须为具体的header名称,逗号分隔。不会报错提示不能为*,但是不生效
Access-Control-Expose-Headers
// 除了GET、POST、HEAD之外的METHOD名称,逗号分隔。不会报错提示不能为*,但是不生效
Access-Control-Allow-Method

三、几种资源产生跨域后具体表现

1、字体跨域

a.com试图加载一个b.com下的字体时:

// a.com
@font-face {
  font-family: 'my-font';
  src: url(http://b.com/Alibaba-PuHuiTi-Regular.ttf);
}

浏览器控制台会抛出下面的跨域错误:
在这里插入图片描述
在这里插入图片描述
此时我们只需要在字体所在的服务端b.com设置响应头,对a.com进行授权,即可使其正常访问:

add_header Access-Control-Allow-Origin 'http://a.com/';

2、图片的跨域

图片的查看是不会产生跨域的,跨域主要限制的是图片的下载。
当图片是同源时,我们可以通过用a标签结合download属性进行下载:

<a href="//a.xkw.com/1.jpg" download="1.jpg">下载图片</a>

当图片是不同源时,上面的方法只能打开图片,而不会进行下载。此时我们可以结合canvas绘图下载:

//a.com页面
const canvas = document.getElementById('canvas');
const cxt = canvas.getContext("2d");
const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = function () {
  cxt.drawImage(img, 0, 0);
  const image = canvas.toDataURL("image/png")
  const dlLink = document.createElement('a');
  dlLink.download = 'testfile';
  dlLink.href = image;
  document.body.appendChild(dlLink);
  dlLink.click();
  document.body.removeChild(dlLink);
};
img.src = '//b.com/1.jpg';

这其中最重要的一行代码便是img.crossOrigin = 'anonymous',表示以CORS的方式去请求这个张图片,而且服务端需要设置CORS的响应头:Access-Control-Allow-Origin: https://a.com,如果客户端未设置img.crossOrigin = 'anonymous' 则会报错:
在这里插入图片描述
意为污染的画布不可以被导出为base64编码格式,因为上面绘制了跨域的图片。同理,如果服务端未设置相应的响应头,也还是跨域,会报错。

3、JavaScript文件的跨域

虽然JavaScript的执行并不会受到跨域的限制,但如果宿主页面想要捕获到引入的js文件中抛出的异常,此时就需要配置CORS,也就是给<scriot>标签增加crossorigin="anonymous"属性:

<script src="//b.com/b.js" crossorigin="anonymous"></script>

对应的js文件也必须返回对应的CORS响应头:

Access-Control-Allow-Origin: http://a.com或者*

配置完成后,宿主页面可以通过下面的代码获取到完整的异常信息:

window.onerror = function (msg, url, line) {
  console.log(msg, url, line);
}

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

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

相关文章

G 蛋白偶联受体与小分子化合物的相互作用

化学遗传学 (Chemogenetics) 是指一种蛋白被改造与先前未被识别的小分子化合物相互作用的过程。多种蛋白的改造已被报道&#xff0c;包括激酶、非激酶的酶类、G 蛋白偶联受体 (GPCRs) 和配体门控离子通道。化学遗传学技术 DREADDs (Designer receptors exclusively activated b…

MapReduce分区、排序、Combiner

Shuffle MapReduce的Map阶段与Reduce阶段之间有一个Shuffle的过程&#xff0c;包括分区、排序等内容。数据从Map阶段出来后&#xff0c;会进入一个环形缓冲区&#xff08;默认100M&#xff09;&#xff0c;环形缓冲区中会同时记录数据和索引&#xff0c;当使用了80%的时候&…

PostgreSQL主从数据库数据同步

运行环境 操作系统&#xff1a;Debian 11.5 数 据 库&#xff1a;PostgreSQL 14.6 主数据库&#xff1a;192.168.8.68 从数据库&#xff1a;192.168.8.69 使用apt-get安装postgresql&#xff0c;安装方法可以参考 https://blog.csdn.net/itbs/article/details/127909359?…

智能家居环境小护士(原理图、pcb、源码、设计报告)

目录 ARM-STM32校园创新大赛 1 题 目&#xff1a; 智能家居环境小护士 1 摘要 1 引言 2 系统方案 3 整套系统的工作原理是&#xff1a;单片机是整套系统的控制核心&#xff0c;温湿度传感器负责测试环境中的温湿度&#xff1b;烟雾传感器负责检测空气中的有毒气体&#xff0c;…

数据同步工具DataX介绍和原理

目录1. DataX介绍2. 框架设计3. 架构1. DataX介绍 DataX是一个各种数据源之间的离线数据同步工具 DataX的设计理念是一种星型数据链路。DataX作为中间传输载体负责连接各种数据源&#xff0c;通过reader从一个数据源读取数据&#xff0c;再通过writer将数据写入另一个数据源。…

Hadoop运行模式

hgfhfg Hadoop运行模式包括&#xff1a;本地模式、伪分布式模式以及完全分布式模式。 Hadoop官方网站&#xff1a;Apache Hadoop 一、本地运行模式 官方Grep案例 1. 创建在hadoop-2.7.2文件下面创建一个input文件夹 mkdir input 2. 将Hadoop的xml配置文件复制到input cp et…

FTP替代产品方案的优异性体现在哪些方面?

多年来&#xff0c;FTP一直是最常见的交换文件的方式&#xff0c;FTP-FTPS-SFTP似乎是FTP的不断迭代更新&#xff0c;但是究竟是技术更新导致FTP过时&#xff1f;还是它真的已经满足不了企业的需求了&#xff1f; 之前&#xff0c;大家选择FTP往往是因为它简单易得的特性&…

在 MySQL 中模拟外部联接 (LEFT、RIGHT、INNER JOIN、OUTER JOIN)

上周的文章详细介绍了 SELECT 查询中的外部联接。它是一种 JOIN 类型&#xff0c;可以从相关表中返回匹配和不匹配的行。遗憾的是&#xff0c;并非所有数据库&#xff08;DB&#xff09;供应商都支持它&#xff0c;包括 MySQL。但这没关系&#xff0c;因为可以通过组合其他三种…

【Java】构造方法及类的初始化

一. 利用构造方法给对象初始化 1. 构造方法的概念 构造方法(也称为构造器)是一个特殊的成员方法&#xff0c;其名字必须与类名相同&#xff0c;在创建对象时&#xff0c;由编译器自动调用&#xff0c;并且在整个对象的生命周期内只调用一次。 构造方法的作用就是给对象中的成…

心知天气api接口怎么用?

心知天气是什么&#xff1f;心知天气提供API吗&#xff1f; 心知天气是国内领先的气象服务商&#xff0c;由中国气象局官方授权的商业气象服务公司&#xff0c;基于气象数值预报和人工智能技术&#xff0c;提供高精度气象数据、天气监控机器人、气象数据可视化产品&#xff0c…

基于PHP+MySQL美食分享网站的设计与实现(含论文)

本系统是一个基于PHP和MySQL的美食分享网站,在本网站中用户可以通过注册登录来查看其他人分享的美食,查看周边好吃的店铺,分享和管理自己的美食,并且可以对他人分享的美食进行评论等一系类操作,通过这些操作可以让大家更加愉快的就美食进行交流 通过上图我们可以看到美食网站的…

Web APIs——DOM

JS 的组成 Web API Web API 是浏览器提供的一套操作浏览器功能和页面元素的 API ( BOM 和 DOM )。 现阶段我们主要针对于浏览器讲解常用的 API , 主要针对浏览器做交互效果。 比如我们想要浏览器弹出一个警示框&#xff0c; 直接使用 alert(‘弹出’) MDN 详细 API : https://d…

如何在Github精准地搜索项目

文章目录1、Github的项目有什么组成&#xff1f;2、如何搜索&#xff1f;in:name 条件in:readme 条件in:description 条件language:条件pushed: 条件stars: 条件awesome 关键字3、查看阅读项目https://blog.csdn.net/qq_45069279/article/details/107809617 https://blog.csdn.…

[附源码]SSM计算机毕业设计高校教师教学助手系统的设计与实现JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

将Nacos注册到springboot使用以及Feign实现服务调用

哈喽~大家好&#xff0c;这篇来看看将Nacos注册到springboot使用以及Feign实现服务调用。 &#x1f947;个人主页&#xff1a;个人主页​​​​​ &#x1f948; 系列专栏&#xff1a;【微服务】 &#x1f949;推荐专栏&#xff1a; JavaEE框架 目录 …

8 年 Java 开发含泪刷题,架构岗现在好难进,有点崩溃

架构岗现在好难进&#xff0c;有点崩溃了。一位粉丝后台留言道。具体问了下情况&#xff0c;是一位 8 年工作经验的朋友&#xff0c;代码功底扎实&#xff0c;项目经验也积累了不少。 为什么要用分布式锁&#xff1f;分布式锁的释放&#xff0c;需要注意什么&#xff1f;锁的过…

Elastic:总结收集日志的几种方法

到目前为止&#xff0c;我们看到有很多中不同的方法来收集日志。甚至&#xff0c;我们针对同样的一个日志&#xff0c;有好多种方法来进行采集。在今天的这篇文章中&#xff0c;我来简单里回顾一下。 通过 Filebeat 采集 Filebeat 是最为常用的一种采集日志的方法。使用 Fileb…

市面上哪种耳机适合跑步用、五款最适合跑步用的蓝牙耳机分享

对于很多运动爱好者来说&#xff0c;跑步&#xff0c;就像吃饭一样&#xff0c;已经成为生活中非常重要的习惯&#xff0c;跑步时听听音乐&#xff0c;让跑步的过程更加愉悦&#xff0c;但是你的运动耳机选对了嘛&#xff1f;首先我们要知道一款专业的运动耳机&#xff0c;一定…

Zookeeper:Mac通过Docker安装Zookeeper集群

此篇为 “Mac通过Docker安装Zookeeper集群”&#xff0c;笔者原本计划是接下来更新Zookeeper应用系列的相关内容&#xff0c;但相关内容依赖Zookeeper集群&#xff0c;虽然前面也更新了 Linux下Zookeeper在三种模式下的部署&#xff0c;但是大家很可能不会有相关的Linux集群准备…