同源策略与解决方法

同源策略与解决方法

1.浏览器的同源策略

1.1 同源策略

同源策略(same origin policy),一种安全策略,用于限制一个源的文档或者它加载的脚本如何能与另一个源的资源进行交互。

浏览器默认两个不同的源之间是可以互相访问资源和操作 DOM 的。两个不同的源之间若是想要访问资源或者操作 DOM,就会受到同源策略的制约。

1.2 同源

同源:URL 的协议、主机、端口号相同。

解释一下协议、主机、端口号:

  • 协议:协议是定义了数据如何在计算机内和之间进行交换的规则的系统,例如 HTTP、HTTPS。
  • 主机:是已连接到一个计算机网络的一台电子计算机或其他设备。网络主机可以向网络上的用户或其他节点提供信息资源、服务和应用。使用 TCP/IP 协议族参与网络的计算机也可称为 IP 主机。
  • 端口:主机是计算机到计算机之间的通信,那么端口就是进程到进程之间的通信。

1.3 限制

同源策略的限制:

  • DOM 访问限制:DOM 和 Js 对象无法获得。同源策略限制了网页脚本(如 JavaScript)访问其他源的 DOM。这意味着通过脚本无法直接访问跨源页面的 DOM 元素、属性或方法。(防止恶意网站从其他网站窃取敏感信息)
  • Web 数据限制:Cookie、LocalStorage 和 IndexDB 无法读写。限制从其他源加载的 Web 数据(例如 XMLHttpRequest 或 Fetch API)。在同源策略下,XMLHttpRequest 或 Fetch 请求只能发送到与当前网页具有相同源的目标。(这有助于防止跨站点请求伪造(CSRF)等攻击)
  • 网络通信限制:AJAX 请求不能发送,浏览器会阻止从一个源发出的请求获取来自其他源的响应。这样做是为了确保只有受信任的源能够与服务器进行通信,以避免恶意行为。

2. 跨域解决方案

2.1 ajax 跨域请求方案 jsonp

jsonp(JSON with Padding),是 JSON 的一种 “使用模式”,可以让网页跨域读取数据,其本质是利用 script 标签的开放策略,浏览器传递 callback 参数到后端,后端返回数据时会将 callback 参数作为函数名来包裹数据,从而浏览器就可以跨域请求数据并制定函数来自动处理返回数据。

在这里插入图片描述

demo:

var script = document.createElement('script');
script.type = 'text/javascript';
// 传参callback给后端,后端返回时执行这个在前端定义的回调函数
script.src = 'http://a.qq.com/index.php?callback=handleCallback';
document.head.appendChild(script);
// 回调执行函数
function handleCallback(res) {
  alert(JSON.stringify(res));
}

优点:

  • jsonp 兼容性强,适用于所有浏览器,尤其是 IE10 以下浏览器

缺点:

  • 没有关于调用错误的处理
  • 只支持 GET 请求,不支持 POST 以及大数据量的请求,也无法拿到相关的返回头,状态码等数据
  • callback 参数恶意注入,可能会造成 xss 漏洞
  • 无法设置资源访问权限

2.2 跨域资源共享(CORS)

浏览器限制

情景:跨站请求正常发送,但是结果被浏览器拦截。

原因:浏览器将不同域的内容隔离在不同的进程中,网络进程负责下载资源并将其送到渲染进程中,但由于跨域限制,某些资源可能被阻止加载到渲染进程。如果浏览器发现一个跨域响应包含了敏感数据,它可能会阻止脚本访问这些数据,即使网络进程已经获得了这些数据。CORB 的目标是在渲染之前尽早阻止恶意代码获取跨域数据。

CORS

跨源资源共享(Cross-Origin Resource Sharing,CORS)是一种允许在受控的条件下,不同源的网页能够请求和共享资源的机制。

CORS 整个通信过程都是浏览器自动完成,浏览器一旦发现 ajax 请求跨源,就会自动在头信息中增加 Origin 字段,用来说明本次请求来自哪个源(协议+域名+端口)。因此,实现 CORS 通信的关键是服务器,需要服务器配置 Access-Control-Allow-Origin 头信息。
基本思想:

  • 服务器在响应中提供一个标头(HTTP 头),指示哪些源被允许访问资源。
  • 浏览器在发起跨域请求时会先发送一个预检请求(OPTIONS 请求)到服务器,服务器通过设置适当的 CORS 标头来指定是否允许跨域请求,并指定允许的请求源、方法、标头等信息。

CORS 的请求根据是否会触发 OPTIONS 请求,分为两类:

  • 简单请求:不触发 CORS 的请求
  • 预检请求:在正式通信之前,增加一次 OPTIONS 查询请求。

简单请求

需要满足的条件:

  • HTTP 方法限制:只能使用 GET、HEAD、POST 这三种 HTTP 方法之一。如果请求使用了其他 HTTP 方法,就不再被视为简单请求。
  • 自定义标头限制:请求的 HTTP 标头只能是以下几种常见的标头:Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type(仅限于 application/x-www-form-urlencoded、multipart/form-data、text/plain)。HTML 头部 header field 字段:DPR、Download、Save-Data、Viewport-Width、WIdth。如果请求使用了其他标头,同样不再被视为简单请求。
  • 请求中没有使用 ReadableStream 对象。
  • 不使用自定义请求标头:请求不能包含用户自定义的标头。
  • 请求中的任意 XMLHttpRequestUpload 对象均没有注册任何事件监听器;XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问

预检请求

  • 预检请求:在正式通信之前,增加一次 OPTIONS 查询请求。服务器通过了 预检请求,以后每次浏览器正常的 CORS 请求,就都跟简单请求一样,会有一个 Origin 头信息字段。服务器的回应,也都会有一个 Access-Control-Allow-Origin 头信息字段。

第一个类型为 preflight 就是预检请求:

在这里插入图片描述

查看预检请求的请求头数据:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

OPTIONS 请求头中的特殊字段:

  • ccess-Control-Request-Method:该字段是必须的,用来列出浏览器的 CORS 请求会用到哪些 HTTP 方法。
  • Access-Control-Request-Headers:该字段是一个逗号分隔的字符串,指定浏览器 CORS 请求会额外发送的头信息字段。即告知服务器,实际请求将携带的自定义请求首部字段。

查看预检请求的响应头数据:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

OPTIONS 响应头中的特殊字段:

  • Access-Control-Allow-Credentials(可选):值是一个布尔值,表示是否允许发送 Cookie。默认情况下,Cookie 不包括在 CORS 请求之中。设为 true,即表示服务器明确许可,前端也需要设置 withCredentials,Cookie 可以包含在请求中,一起发给服务器。这个值也只能设为 true,如果服务器不要浏览器发送 Cookie,删除该字段即可。
  • Access-Control-Expose-Headers(可选):CORS 请求时,XMLHttpRequest 对象的 getResponseHeader()方法只能拿到 6 个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在 Access-Control-Expose-Headers 里面指定。
  • Access-Control-Allow-Methods:表明服务器允许客户端使用什么方法发起请求
  • Access-Control-Allow-Origin:(必须):表示可以请求的数据,可以设置为* 符号,表示允许任意跨源请求。
  • Access-Control-Allow-Headers:将实际请求所携带的首部字段告诉服务器

OPTIONS 请求的更多信息,可参考官方文档

在这里插入图片描述

优点与缺点

优点:

  • 支持所有类型的 HTTP 请求,功能完善。
  • 通过 onerror 事件监听进行调用错误处理;
  • 通过 Access-Control-Allow-Origin 进行资源访问授权。

缺点:

  • 目前主流浏览器(IE10 及以上)都支持 CORS,但 IE8 和 IE9 需要使用 XDomainRequest 对象进行兼容,IE7 及以下浏览器不支持。

2.3 服务器代理

后端进行代理中转请求至服务器端,然后将获取的数据返回给前端。

外网前端页面无法访问内网接口,配置代理接口允许前端页面访问,并中转内网接口,则外网前端页面可以跨域访问内网接口。

比如配置 Nginx,将接收到的请求转发到内网:

    server{
        #如果是静态文件,直接指向目录
        location / {
            root   html;
            index  index.html index.htm;
        }
        # 如果是动态应用,用proxy_pass转发一下
        location ~ ^/api/(.*?)$ {
             proxy_pass  http://127.0.0.1:8080/api/$1?$args;
        }
    }

优点:

  • 前端无需进行任何改变

缺点:

  • 后端需要一定工作量

2.4 前端跨域

前端跨域通信是指浏览器中两个不符合同源策略的前端页面进行通信。

2.4.1 document.domain+iframe

仅适用于主域相同,子域不同的前端通信跨域场景。

核心点:

  • A 嵌套 B
  • 将 document.domain 指向主域名

页面 A:

<!-- A页面 http://a.qq.com/a.html -->
<iframe id="iframe" src="http://b.qq.com/b.html"></iframe>
<script>
  document.domain = 'qq.com';
  var windowB = document.getElementById('iframe').contentWindow;
  alert('B页面的user变量:' + windowB.user);
</script>

页面 B:

<!-- B页面 http://b.qq.com/b.html -->
<script>
  document.domain = 'qq.com';
  var user = 'saramliu';
</script>

2.4.2 location.hash+iframe

利用 url 的 hash 值改变但不刷新页面的特性,实现简单的前端跨域通信。

受到浏览器安全机制的限制,A 嵌套 B,A 可以修改 B 的 hash 值,但 B 不能修改 A 的 Hash 值,需要一个与 A 同源的页面来中转。

A 页面:

<!-- A页面 http://a.qq.com/a.html -->
<iframe id="iframe" src="http://b.qq1.com/b.html"></iframe>
<script>
  // 监听c.html传来的hash值
  window.onhashchange = function () {
    alert('B页面传递数据:' + location.hash.substring(1));
  };
</script>

B 页面:

<!-- B页面 http://b.qq1.com/b.html -->
<iframe id="iframe" src="http://a.qq.com/c.html"></iframe>
<script>
  // 向c.html传递hash值
  var iframe = document.getElementById('iframe');
  setTimeout(function () {
    iframe.src = iframe.src + '#user=saramliu';
  }, 1000);
</script>

中转页面:

<!-- C页面 http://a.qq.com/c.html -->
<script>
  // 监听b.html传来的hash值
  window.onhashchange = function () {
    // 操作同域a.html的hash值,传递数据
    window.parent.parent.location.hash = window.location.hash.substring(1);
  };
</script>

优点:

  • 可以解决主域不同的前端通信跨域问题。
  • hash 改变,页面不会刷新。

缺点:

  • 受部分浏览器安全机制限制,需要额外的同源中转页面,且中转页面需要 js 逻辑来修改 hash 值。
  • 通信数据类型及长度均受限,且数据外显在 url 上,存在一定安全风险。

2.4.3 window.name+iframe

window.name 属性在不同页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。

A 嵌套 B,B 将要传递的数据附加到 window.name 上,然后跳转到与 A 同域名的 C,A 和 C 满足同源策略,A 可与获取到 C 的 window.name。

A

<!-- A页面 http://a.qq.com/a.html -->
<iframe id="iframe" src="http://b.qq1.com/b.html"></iframe>
<script>
  var state = 0;
  var iframe = document.getElementById('iframe');
  iframe.onload = function () {
    if (state === 1) {
      // 第2次onload成功后,读取同域window.name中数据
      alert(iframe.contentWindow.name);
    } else if (state === 0) {
      // 第1次onload成功后
      state = 1;
    }
  };
</script>

B:

<!-- B页面 http://b.qq1.com/b.html -->
<script>
  window.name = '这里是B页面!';
  window.location = 'http://a.qq.com/c.html'; // 跳转到与A同源的C
</script>

优点:

  • 通信数据类型不受限,且长度可达 2MB。

缺点:

  • 需要额外的同源中转页面,但中转页可以为空白页。

2.4.4 postMessage

postMessage 是 HTML5 XMLHttpRequest Level2 中的 API,是一种安全的跨域通信方法,且是为数不多可以跨域操作的 window 属性之一,它通常用于解决以下方面的问题:

  • 页面和其打开的新窗口的数据传递。
  • 多窗口之间消息传递。
  • 页面与嵌套 iframe 消息传递。

A 获得 B 的 window 对象后,A 调用 postMessage 方法发送一个个 MessageEvent 消息。B 通过监听 message 事件即可获取 A 传递的数据。

A:

<iframe id="iframe" src="http://b.qq1.com/b.html"></iframe>
<script>
  var iframe = document.getElementById('iframe');
  iframe.onload = function () {
    var data = { meesage: '这里是A页面发的消息' };
    var url = 'http://b.qq1.com/b.html'; // 向B页面发送消息
    iframe.contentWindow.postMessage(JSON.stringify(data), url);
  };
  window.addEventListener('message', function (e) {
    alert('B页面发来消息:' + JSON.parse(e.data));
  });
</script>

B:

<!-- B页面 http://b.qq1.com/b.html -->
<script>
  window.addEventListener(
    'message',
    function (e) {
      alert('A页面发来消息:' + JSON.parse(e.data));
      var data = { meesage: '这里是B页面发的消息' };
      var url = 'http://a.qq.com/a.html';
      window.parent.postMessage(JSON.stringify(data), url);
    },
    false
  );
</script>

优点:

  • 可以解决多种类型的前端跨域通信问题。

缺点:

  • 兼容性方面相对差一点,IE8 及以下浏览器不支持该方法,IE9 只支持 postMessage 传递 string 类型的数据,而标准的 postMessage 消息数据可以是任何类型。

参考文档

  • 同源策略(same origin policy)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【面试题】前端面试复习6---性能优化

前端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 性能优化 一、性能指标 要在 Chrome 中查看性能指标&#xff0c;可以按照以下步骤操作&#xff1a; 打开 Chrome 浏览器&#xff0c;并访问你想要测试…

Zabbix下载安装及SNMP Get使用

帮助文档&#xff1a;6. Zabbix Appliance 一、zabbix下载安装 1、获取Zabbix Appliance镜像 Download Zabbix appliance 2、使用该镜像创建虚拟机 3、打开虚拟机控制台自动安装&#xff0c;等待安装完成即可 默认配置 系统/数据库&#xff1a;root:zabbix Zabbix 前端&am…

ROS中使用Navigation报错信息

在ROS中使用遇到了几个Navigation报错信息&#xff0c;在这里进行下记录&#xff1a; [ WARN] [1688134727.429227824]: The origin for the sensor at (7.35, 13.12) is out of map bounds. So, the costmap cannot raytrace for it. 解决办法&#xff1a; [ WARN] [16881…

ChatGPT的局限性及商业化应用限制讨论

首先&#xff0c;ChatGPT仅使用公开可用的信息&#xff0c;这是其第一个局限。如果基础信息缺失、过时、模糊或过于泛化&#xff0c;AI生成的内容就将不会准确。 只有在使用企业内部专有信息和知识创建特定的GPT时&#xff0c;才会出现真正的商业化解决方案。但对企业而言&…

每日一题:leetcode 1267 统计参与通信的服务器

这里有一幅服务器分布图&#xff0c;服务器的位置标识在 m * n 的整数矩阵网格 grid 中&#xff0c;1 表示单元格上有服务器&#xff0c;0 表示没有。 如果两台服务器位于同一行或者同一列&#xff0c;我们就认为它们之间可以进行通信。 请你统计并返回能够与至少一台其他服务…

上传WSL项目到gitlab

上传WSL项目到gitlab 设置ssh将SSH公钥添加到Gitlab 将WSL上的代码上传到gitlab确保在WSL环境中安装了git下面是上传代码到GitLab的具体步骤&#xff1a; 可能遇到的各种错误 设置ssh Gitlab添加SSH KEY 什么是SSH ? SSH 是一种网络协议&#xff0c;具备协议级别的认证及会话…

【【萌新的STM32学习-19-蜂鸣器实验】】

萌新的STM32学习-19-蜂鸣器实验 STM32在正点原子的视频中并未讲述关于蜂鸣器的实验&#xff0c;我们自己探究一下作为简单的HAL库入门 蜂鸣器每隔 300ms 响或者停一次。LED0 每隔 300ms 亮或者灭一次。LED0 亮的时候蜂鸣器不叫&#xff0c;而 LED0 熄灭的时候&#xff0c;蜂鸣…

【Interaction交互模块】AngularJointDrive角度关节驱动

文章目录 一、预设体位置二、案例&#xff1a;做一个“能开合的门” 1、在已建好的门框下&#xff0c;建门 2、设置参数 3、解决产生的问题 一、预设体位置 交互模块——可控制物体——物理关节——角度关节驱动 二、案例&#xff1a;做一个“能开合的门” 1…

2023常见前端面试题

以下是一些2023年秋招常见的前端面试题及其答案&#xff1a; 1. 请解释一下什么是前端开发&#xff1f; 前端开发是指使用HTML、CSS和JavaScript等技术来构建网页和用户界面的过程。前端开发人员负责将设计师提供的视觉设计转化为可交互的网页&#xff0c;并确保网页在不同设备…

xsschallenge1~13通关详细教程

文章目录 XSS 挑战靶场通关level1level2level3level4level5level6level7level8level9level10level11level12level13 XSS 挑战靶场通关 level1 通过观察发现这个用户信息可以修改 那么我们直接输入攻击代码 <script>alert(/wuhu/)</script>弹框如下&#xff1a; …

树与图c++

1.树 前言 本文主要介绍的数据结构之树型结构的相关知识&#xff0c;树型数据结构是面试官面试的时候非常喜欢考的一种数据结构&#xff0c;树形结构的遍历也是大厂笔试非常喜欢设置的考点&#xff0c;这些内容都会在本篇文章中进行详细的介绍&#xff0c;并且还会介绍一些常…

线程安全-搞清synchronized的真面目

多线程编程中&#xff0c;最难的地方&#xff0c;也是最重要的一个地方&#xff0c;还是一个最容易出错的地方&#xff0c;更是一个特别爱考的地方&#xff0c;就是线程安全问题。 万恶之源&#xff0c;罪魁祸首&#xff0c;多线程的抢占式执行,带来的随机性. 如果没有多线程,此…

【VRRP】虚拟路由冗余协议

什么是VRRP&#xff1f; 虚拟路由冗余协议VRRP&#xff08;Virtual Router Redundancy Protocol&#xff09;是一种用于提高网络可靠性的容错协议。通过VRRP&#xff0c;可以在主机的下一跳设备出现故障时&#xff0c;及时将业务切换到备份设备&#xff0c;从而保障网络通信的…

Redis基础知识

Redis基础知识 redis共有16个数据库&#xff0c;默认使用的是第0个 可以使用select进行切换数据库 # 切换数据库 127.0.0.1:6379> select 1 OK # 查看DB大小 127.0.0.1:6379[1]> DBSIZE (integer) 0查看当前数据库所有的key 127.0.0.1:6379> keys * #查看当前数据…

Mybatis查询数据

上一篇我们介绍了在pom文件中引入mybatis依赖&#xff0c;配置了mybatis配置文件&#xff0c;通过读取配置文件创建了会话工厂&#xff0c;使用会话工厂创建会话获取连接对象读取到了数据库的基本信息。 如果您需要对上面的内容进行了解&#xff0c;可以参考Mybatis引入与使用…

【业务功能篇87】微服务-springcloud-本地缓存-redis-分布式缓存-缓存穿透-雪崩-击穿

一、缓存 1. 什么是缓存 缓存的作用是减低对数据源的访问频率。从而提高我们系统的性能。 缓存的流程图 2.缓存的分类 2.1 本地缓存 其实就是把缓存数据存储在内存中(Map <String,Object>).在单体架构中肯定没有问题。 单体架构下的缓存处理 2.2 分布式缓存 在分布式环…

Java学习笔记31——字符流

字符流 字符流为什么出现字符流编码表字符串中的编码解码问题字符流写数据的5中方式字符流读数据的两种方式字符流复制Java文件 字符流 为什么出现字符流 汉字的存储如果是GBK编码占用2个字节&#xff0c;如果是UTF-8占用三个字节 用字节流复制文本文件时&#xff0c;文本文…

2023年腾讯云轻量应用服务器优缺点大全

2023年腾讯云轻量应用服务器优缺点大全&#xff0c;腾讯云轻量应用服务器性能如何&#xff1f;轻量服务器CPU内存带宽配置高&#xff0c;CPU采用什么型号主频多少&#xff1f;轻量应用服务器会不会比云服务器CVM性能差&#xff1f;腾讯云服务器网详解CPU型号主频、内存、公网带…

Linux通过libudev获取挂载路径、监控U盘热拔插事件、U盘文件系统类型

文章目录 获取挂载路径监控U盘热拔插事件libusb 文件系统类型通过挂载点获取挂载路径添libudev加库 获取挂载路径 #include <stdio.h> #include <libudev.h> #include <string.h>int main() {struct udev *udev;struct udev_enumerate *enumerate;struct ud…

数据库备份和Shell基础测试及AWK(运维)

第一题&#xff1a;简述一下如何用mysql命令进行备份和恢复&#xff0c;请以test库为例&#xff0c;创建一个备份&#xff0c;并再用此备份恢复备份 备份步骤&#xff1a; 备份test库&#xff1a;使用mysqldump命令备份test库&#xff0c;并将备份写入一个.sql文件中。命令示例…