| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 网络协议 -> 前端网络基础 - 跨域xhr/fetch -> 正文阅读 |
|
[网络协议]前端网络基础 - 跨域xhr/fetch |
目录 浏览器的同源策略同源策略是浏览器特有的一种安全机制,它主要用于限制不同的源之间的数据交互。 举个例子 http://192.168.3.154:5500/index.html 想去访问 http://192.168.3.9:80/user,会被浏览器限制。 当?http://192.168.3.154:5500/index.html 去访问?http://192.168.3.9:80/user 时 可以发现,浏览器端报错CORS error,即Cross-origin resource sharing 跨源资源共享发生错误,错误原因就是浏览器同源策略限制了本次不同源间数据的交互。 那么浏览器具体是如何限制的呢? 通过服务器端的日志
我们可得: 浏览器没有限制非同源请求的发送,浏览器也没有限制服务器的响应动作,浏览器只是限制了网页接收非同源的响应。 即:服务器的响应被浏览器收到了,但是浏览器没有将非同源响应交给发起的网页。 如何定义同源同源:即 protocol(协议)、host(主机)、port(端口)相同 我们通过下面的例子来深入理解下同源 浏览器网页 http://192.168.3.9/index.html 请求以下源时,是否会发生CORS error,即判断二者是否同源?
另外,我们还需要注意 IP地址和其对应域名之间是否算同源? 我们设置 IP地址 192.168.3.9?的域名为?www.qfc.com 这里我将本地主机的DNS解析文件hosts中配置增加一行如上,则可以保证www.qfc.com会被解析为192.168.3.9 然后修改服务器代码,让服务器既可以提供前台页面index.html,也可以提供服务器业务处理接口app.js,保证index.html和app.js中接口同源 如下app.js中,提供了index.html访问路由,以及/user接口访问路由? index.html中fetch请求http://www.qfc.com/user接口 ?但是浏览器访问的http://192.168.3.9/index.html 可以发现,即使http://www.qfc.com/user请求的主机是自身IP对应的域名,也会被视为不同源。 什么是跨域跨域其实就是同源的反义词,跨域可以理解为非同源。 随着前后端分离的发展,前端独立部署,访问非同源的后端服务器,已经是一种普遍现象。 但是由于浏览器同源策略的影响,跨域请求被限制了,所以我们需要一种可以规避同源策略,实现跨域请求的解决方案。 JSONP跨域script标签的特点在HTML中,有一些标签也可以发起HTTP请求,比如script标签,link标签,img标签,form标签,且被允许跨域。
其中,script标签由于其可以执行引入的js文件的代码,再加上其跨域特性,让script标签可以用来做一些超出其设计初衷的事。 script标签会发起HTTP GET去请求服务器上的js文件,所以script标签可以用于实现HTTP GET跨域请求。 如下例子,我们使用script引入了非同源的某cdn服务器上的axios.min.js,然后使用axios发起xhr请求同源接口 ?可以发现,虽然axios.min.js是非同源服务器上的,但是script标签也可以跨域引入。 JSONP实现原理那么如何使用script标签实现普适的跨域请求呢? 啥叫普适的跨域请求,举个例子,我们可以利用script标签请求上例中http://192.168.3.9/user接口,并获得接口响应。 这个过程,有两个点,一是发送HTTP GET请求http://192.168.3.9/user,这点不难,直接将http://192.168.3.9/user作为script标签src值即可 还有一点,如何获取http://192.168.3.9/user返回值? 我们知道常规的http://192.168.3.9/user接口返回的就是一个文本内容,这个文本内容可能是html字符串,也可能是json字符串,或者其他,但是script标签期望引入的文件内容是js代码,这样就可以执行了。 所以,我们可以将实际文本内容的HTTP响应,封装进js函数入参中,这样script标签引入后,就会自动执行js函数,这样我们就可以收到入参了。 如下示例 http://127.0.0.1:5500/index.html 通过script标签HTTP GET请求 http://192.168.3.9/user,得到响应文本 'fn("get user success")',script标签会将 'fn("get user success")'作为js代码执行,然后调用fn函数,而我们又在http://127.0.0.1:5500/index.html提前准备了一个fn函数,所以"get user success"会被作为入参传递到fn函数中。 这样就完成了依靠script标签进行跨域普适请求。 但是以上代码有一处不足,就是前端和后端需要约定好函数的名字,比如fn,而fn其实并不是业务需要的,所以没必要进行前后端约定。 我们知道HTTP GET请求可以在URL中携带参数,所以我们只要将函数名字作为URL查询参数传递到后端,后端就无需关心函数名了。 ?以上基于script标签实现的跨域纯属于钻漏洞,是一种无奈之举,这种跨域方案被命名为JSONP。 JSONP的缺点JSONP的缺点也很明显,只能实现HTTP GET请求的跨域,对于其他请求方式无能为力,因为script标签只能发起HTTP GET请求。 另外JSONP要求前后端协作,前端和后端代码开发起来都很别扭,不符合规范的代码要求。 同时由于JSONP实现了跨域,所以很容易被恶意网站利用,因为任何网站都可以通过script标签引入可以跨域的目的源接口,有很大的安全问题,除非后端对请求源做限制。 JSONP的安全问题防护策略这里演示下使用请求头中的Referer信息来做校验,即服务器端根据请求头中的Referer来判断请求源,并限制可以跨域的Referer源,如下trusted_domain是可信源,如果不在可信源的Referer则响应401,无权操作 ? 下面在http://127.0.0.1:5500/index.html?通过script标签 请求 http://192.168.3.9/user,结果得到响应401 而在http://192.168.3.154:5500/index.html就可以正常访问http://192.168.3.9/user 以上就是服务器端通过请求头中Referer信息判断请求源的协议,主机,端口,来进行限制访问。 但是HTTP请求头中的Referer信息是可以被篡改的(比如使用burpsuite),所以基于Referer来判断是不可信的。 其实对于服务器来说,来自前端的请求信息都不可信,无论是origin请求头,还是referer请求头,它们都可能被篡改,所以我们不应该让服务器来做非同源校验。而真正适合非同源校验的还得是浏览器,因为第一手请求是浏览器发出去的,buripsuite这些拦截工具都是吃的浏览器的剩饭。所以对于浏览器来说,请求头中referer,origin都是可信的。 CORS响应头跨域前面分析了,JSONP跨域并不安全,即使在服务器侧做了请求源限制,意义也不大,因为服务器目前只能靠请求头中的referer或origin来判断请求源,但是请求头中的referer或origin很容易被篡改。 浏览器就是请求的制造者,浏览器可以拿到第一手的请求源信息,且不存在被篡改的风险,所以跨域请求的源限定适合交给浏览器来做,而服务器只需要告诉浏览器允许哪些源的跨域请求即可。 Access-Control-Allow-Origin所以,为了实现安全的跨域资源共享,浏览器和服务器之间约定,当服务器接口需要支持跨域请求时,服务器可以在接口的HTTP响应头中包含Access-Control-Allow-Origin字段,该字段的值就是允许跨域请求的源。 浏览器会根据HTTP响应头中的Access-Control-Allow-Origin获取到允许发起跨域请求的源,并对比实际发起请求的源,若相同则浏览器会将响应正常返回给发起者,若不同,则报错CORS error。 Access-Control-Allow-Origin请求头的值有两种情况 Access-Control-Allow-Origin: * Access-Control-Allow-Origin: <origin> 当值为*时,表示允许所有源进行跨域请求。当值为特定源时,表示只允许该源进行跨域请求。 但是CORS跨域是有要求的,并不是说非同源服务器接口响应头添加了Access-Control-Allow-Origin就可以了。 跨域请求的分类浏览器将请求分为了两类, 简单请求 和 非简单请求。 简单请求需要满足如下条件:
不满足以上条件的请求就是非简单请求。 所以跨域请求也被分为了两类,简单跨域请求 和 非简单跨域请求。 简单跨域请求浏览器遇到简单跨域请求时,会直接发送,并且会在请求头中添加一个Origin字段,该字段用于告知服务器当前请求来自于哪个站点,Origin值包含了发起请求的源的协议、主机、端口。 服务器可以根据请求中的Origin头过滤掉一些不可信源的请求,以减轻服务器的资源消耗。而对于可信源的请求,则走正常的服务器业务流程,并在响应头中添加Access-Control-Allow-Origin:可信源,用于告知浏览器该源可以进行跨域请求。 ? 非简单跨域请求浏览器在遇到非简单请求时(如PUT,DELETE请求,或者content-type:application/json的请求),不会直接发送,而是会先发送一个预检请求。 所谓预检请求preflight,即在发送正式请求之前,先发送一次HTTP OPTIONS请求到服务器,用于预先检测服务器是否支持即将发送的正式请求。 ? 预检请求会在请求头中加入
服务器可以根据以上请求头做相应过滤,以减轻服务器压力。对于符合要求的请求源,请求方法,请求头,服务器需要响应如下头部,预检请求会将如下信息告知浏览器允许该跨域请求的得到响应。
? 如下示例,服务器没有做过滤,直接设置了Access-Control-Allow-相关的响应头,浏览器收到预检请求的响应后,知道了可用的请求方法有GET,PUT,可用的请求头有test1,test2 所以下面请求源,请求方法,请求头都符合要求,所以浏览器将响应正常返回了? ?
如果使用了不可用的头,如test3,则报错CORS error ? 所以即使服务器设置了Access-Control-Allow-Origin,且请求源符合就是其设置的可信源,也不能保证成功的CORS,对于非简单请求而言,还要服务器设置Access-Control-Allow-Headers和Access-Control-Allow-Methods。 对比JSONP跨域CORS响应头跨域更加安全,且支持所有HTTP METHOD,另外只需要服务器在响应中增加几个头字段即可,前端不需要任何改动。 代理服务器跨域?我们需要搞清楚 同源策略 是浏览器内置的安全策略,所有从浏览器发出的请求都要符合同源策略,如果需要通过浏览器发送跨域请求,则需要借助JSONP,CORS等跨域技术,而JSONP,CORS实际上也是基于浏览器的,它们并不是绕过浏览器同源策略,而是走浏览器同源策略开的后门。 那么有没有办法绕过浏览器的同源策略呢? 有,那就是不通过浏览器发出跨域请求。 那么如何实现呢?大致流程如下: 网页? →? 同源服务器? →? 非同源服务器 即由网页同源的代理服务器作为中间件,来转发网页的请求到非同源服务器,这样就绕过了浏览器发送跨域请求。 此时,我们需要注意的是,如果目的服务器自己做了请求源过滤,则我们需要做一些欺骗收到,即设置虚假的referer或origin头来迷惑目的服务器 下面我准备两台服务器: 1、目的服务器 目的服务器没有设置CORS头,也没有做JSONP接口 2、代理服务器? 代理服务器负责提供前台网页(这里使用了express.static静态资源托管负责提供前台网页),并且负责转发请求到目的服务器 我们知道如果直接在http://127.0.0.1/index.html中请求 http://192.168.3.9/user 会报错CORS error,但是现在?http://127.0.0.1/index.html中请求的是同源的代理服务器接口 http://127.0.0.1/user,所以不会报错CORS error。 而代理服务器收到http://127.0.0.1/user 请求后,会立即转发给http://192.168.3.9/user,由于该跨域请求是通过服务器发送的,而不是浏览器,所以不受浏览器同源策略影响。 |
|
网络协议 最新文章 |
使用Easyswoole 搭建简单的Websoket服务 |
常见的数据通信方式有哪些? |
Openssl 1024bit RSA算法---公私钥获取和处 |
HTTPS协议的密钥交换流程 |
《小白WEB安全入门》03. 漏洞篇 |
HttpRunner4.x 安装与使用 |
2021-07-04 |
手写RPC学习笔记 |
K8S高可用版本部署 |
mySQL计算IP地址范围 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/26 7:23:33- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |