同源策略与解决方法

同源策略与解决方法

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)

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

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…

如何为winform控件注册事件

有很多winform的初学者不知道如何为winform注册的事件代码,本篇博文就是以button控件为例子,为winform注册单击事件,如下: 1、新建一个winform 以visual studio 2019 社区版为例子,新建一个winform程序,如下: 关于visual studio 2019 社区版下载方式点击这里:手把手教…

贝叶斯人工智能大脑与 ChatGPT

文章目录 一、前言二、主要内容 &#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 一、前言 论文地址&#xff1a;https://arxiv.org/abs/2308.14732 这篇论文旨在研究 Chat Generative Pre-trained Transformer&#xff08;ChatGPT&#xff09;在贝叶斯…

阿姆达尔定律(Amdahl‘s Law)通俗解释

阿姆达尔定律&#xff08;Amdahl’s Law&#xff09;&#xff0c;它描述了在对系统的某个部分进行加速时&#xff0c;该部分对整体系统性能的影响&#xff0c;取决于该部分的重要性和加速程度。 原书给的例子不太好懂&#xff0c;下面是一个更好懂的例子。 例子&#xff1a;汽车…

C语言:指针类型的意义

1.指针的类型决定了解引用时访问几个字节 2.指针的类型决定了指针1、-1跳过几个字节 一、指针的类型决定指针解引用时访问几个字节 例如 int 型指针解引用时访问4个字节 char 型指针解引用时访问1个字节 详解代码如下&#xff1a; int b 0x11223344&#xff08;十六进制&…

MVVM架构模式

目录 一、MVVM简介二、MVVM结构三、MVC四、MVP五、MVVM的优势与存在的问题 一、MVVM简介 MVVM是Model-View-ViewModel的简写。即是模型-视图-视图模型。 MVVM架构模式是一种软件设计模式&#xff0c;它将应用程序分为三个部分&#xff1a;模型&#xff08;Model&#xff09;、…

【附安装包】CAD2024(建筑版)安装教程

软件下载 软件&#xff1a;CAD建筑版本&#xff1a;2023语言&#xff1a;简体中文大小&#xff1a;4.52G安装环境&#xff1a;Win11/Win10硬件要求&#xff1a;CPU2.5GHz 内存8G(或更高&#xff09;下载通道①百度网盘丨64位下载链接&#xff1a;https://pan.baidu.com/s/1cHe…

大数据治理运营整体解决方案[39页PPT]

导读&#xff1a;原文《大数据治理运营整体解决方案[39页PPT]》&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。 数据治理总体方案 数据治理平台解决方案 数据治理运…

【推荐】Spring与Mybatis集成整合

目录 1.概述 2.集成 2.1代码演示&#xff1a; 3.整合 3.1概述 3.2 进行整合分页 接着上两篇&#xff0c;我已经写了Mybatis动态之灵活使用&#xff0c;mybatis的分页和特殊字符的使用方式接下来把它们集成起来&#xff0c;是如何的呢&#x1f447;&#x1f447;&#x1…

python 单向循环(环形)链表

不带头结点的单向循环链表的示意图 循环链表的应用场景【约瑟夫问题】 现假设 n 5&#xff08;一共有 5 个人&#xff09;&#xff0c;k 1&#xff08;从第 1 个人开始报数&#xff09;&#xff0c; m 2&#xff08;数 2 下&#xff09;&#xff0c;则出队编号的序列为&…

《存储IO路径》专题:IO虚拟化初探

大家好&#xff0c;欢迎来到今天的科技小课堂。今天我们要聊聊的是一项非常有趣且实用的技术——I/O虚拟化&#xff08;Input/Output Virtualization&#xff0c;简称IOV&#xff09;。想象一下&#xff0c;如果把物理硬件资源比作一道丰盛的大餐&#xff0c;那么IOV就是那位神…

【C++初阶】stack的常见操作和模拟实现

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习C和算法 ✈️专栏&#xff1a;C航路 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&#x1…