jsonp-反向代理-CORS解决JS跨域问题的个人总结

jsonp-反向代理-CORS解决JS跨域问题的个人总结

网上说了很多很多,但是看完之后还是很混乱,所以我自己重新总结一下。

解决 js 跨域问题一共有8种方法,

  1. jsonp(只支持 get)
  2. 反向代理
  3. CORS
  4. document.domain + iframe 跨域
  5. window.name + iframe 跨域
  6. window.postMessage
  7. location.hash + iframe
  8. web sockets

各个方法都有各自的优缺点,但是目前前端开发方面比较常用的是 jsonp,反向代理,CORS:

  • CORS是跨源资源分享(Cross-Origin Resource Sharing)的缩写。它是W3C标准,是跨源AJAX请求的根本解决方法。优点是正统,符合标准,缺点是:

    • 但是需要服务器端配合,比较麻烦。
  • JSONP 优点是对旧式浏览器支持较好,缺点是:

    • 但是只支持 get 请求。
    • 有安全问题(请求代码中可能存在安全隐患)。
    • 要确定jsonp请求是否失败并不容易
  • 反向代理都能够兼容以上的确定,但是仅仅作为前端开发模式的时候使用,在正式上线环境较少用到。

    • 因为开发环境的域名跟线上环境不一样才需要这样处理。
    • 如果线上环境太复杂,本身也是多域(后面说到的同源策略问题,多子域,或者多端口问题),那么需要采用 jsonp 或者 CORS 来处理。
这里主要说明这三种方式。其他方式暂不说明。

一、什么是跨域问题

跨域问题一般只出现在前端开发中使用 javascript 进行网络请求的时候,浏览器为了安全访问网络请求的数据而进行的限制。

提示的错误大致如下:

No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://XXXXXX' is therefore not allowed access.

clipboard.png

二、为什么会出现跨域问题

因为浏览器收到同源策略的限制,当前域名的js只能读取同域下的窗口属性。

2.1 同源策略

同源指的是三个源头同时相同:

  • 协议相同
  • 域名相同
  • 端口相同

举例来说,http://www.example.com/dir/page.html这个网址,

协议是 http://
域名是 www.example.com
端口是80 //它的同源情况如下:
http://www.example.com/dir2/other.html:同源
http://example.com/dir/other.html:不同源(域名不同)
http://v2.www.example.com/dir/other.html:不同源(域名不同)
http://www.example.com:81/dir/other.html:不同源(端口不同)

同源策略限制了以下行为:

  • Cookie、LocalStorage 和 IndexDB 无法读取
  • DOM 和 JS 对象无法获取
  • Ajax请求发送不出去
大概可以知道跨域其实就是同源策略导致的,并且知道同源策略的原理。

详细的同源策略相关,可以参考http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html

三、解决跨域问题

3.1 反向代理方式

反向代理和正向代理的区别:

  • 正向代理(Forward Proxy),通常都被简称为代理,就是在用户无法正常访问外部资源,比方说受到GFW的影响无法访问twitter的时候,我们可以通过代理的方式,让用户绕过防火墙,从而连接到目标网络或者服务。
  • 反向代理(Reverse Proxy)是指以代理服务器来接受 Internet 上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给 Internet 请求连接的客户端,此时,代理服务器对外就表现为一个服务器。

那么我们可以理解为反向代理

如何使用反向代理服务器来达到跨域问题解决:

  • 前端ajax请求的是本地反向代理服务器
  • 本地反向代理服务器接收到后:

    • 修改请求的 http-header 信息,例如 referer,host,端口等
    • 修改后将请求发送到实际的服务器
  • 实际的服务器会以为是同源(参考同源策略)的请求而作出处理

现在前端开发一般使用 nodejs来做本地反向代理服务器

// 在 express 之后引入路由
var app = express();var apiRoutes = express.Router();app.use(bodyParser.urlencoded({extended:false}))// 自定义 api 路由
apiRoutes.get("/lyric", function (req, res) {var url = "https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg";axios.get(url, {headers: { // 修改 headerreferer: "https://c.y.qq.com/",host: "c.y.qq.com"},params: req.query}).then((response) => {var ret = response.dataif (typeof ret === "string") {var reg = /^\w+\(({[^()]+})\)$/;var matches = ret.match(reg);if (matches) {ret = JSON.parse(matches[1])}}res.json(ret)}).catch((e) => {console.log(e)})
});// 使用这个路由
app.use("/api", apiRoutes);

3.2 JSONP 方式

JSONP有些文章会叫动态创建script,因为他确实是动态写入 script 标签的内容从而达到跨域的效果:

  • AJAX 无法跨域是受到“同源政策”的限制,但是带有src属性的标签(例如<script>、<img>、<iframe>)是不受该政策限制的,因此我们可以通过向页面中动态添加<script>标签来完成对跨域资源的访问,这也是 JSONP 方案最核心的原理,换句话理解,就是利用了【前端请求静态资源的时候不存在跨域问题】这个思路。
  • JSONP(JSON with Padding)是数据格式JSON的一种“使用模式”。
  • JSONP 只能用 get 方式。

实现 jsonp 的方式:

clipboard.png

引用来自https://segmentfault.com/a/1190000012469713的图

  • 客户端和服务器端约定一个参数名是代表 jsonp 请求的,例如约定 callback 这个参数名。
  • 然后服务器端准备好针对之前约定的 callback 参数请求的 javascript 文件,这个文件里面要有一个函数名,要跟客户端请求的时候的函数名要保持一致。(如下面例子:ip.js
  • 然后客户端注册一个本地运行的函数,并且函数的名字要跟去请求服务器进行 callback 回调的函数的名字要一致。(如下面例子:foo 函数跟请求时候callback=foo的名字是一致的)
  • 然后客户端对服务器端进行 jsonp 的方式请求。
  • 服务器端返回刚才配置好的js 文件(ip.js)到客户端
  • 客户端浏览器,解析script标签,并执行返回的javascript文件,此时数据作为参数,传入到了客户端预先定义好的 callback 函数里。

    • 相当于本地执行注册好foo 函数,然后获取了一个foo 函数,并且这个获取的 foo 函数里面包含了传入的参数(例如 foo({XXXXX})

这是一个实例 demo:

服务器端文件ip.js

foo({"ip": "8.8.8.8"
});

客户端文件 jsonp.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title></title><script>// 动态插入 script 标签到 html 中function addScriptTag(src) {var script = document.createElement('script');script.setAttribute("type","text/javascript");script.src = src;document.body.appendChild(script);}// 获取 jsonp 文件window.onload = function () {addScriptTag('http://example.com/ip?callback=foo');}// 执行本地的 js 逻辑,这个要跟获取到的 jsonp 文件的函数要一致function foo(data) {console.log('Your public IP address is: ' + data.ip);};</script>
</head>
<body>
</body>
</html>

3.3 CORS 方式

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

  • CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
  • 整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器端。只要服务器端实现了CORS接口,就可以跨源通信。

3.3.1 CORS的请求分为两类:

  • 简单请求
  • 非简单请求

只要同时满足以下两大条件,就属于简单请求。

(1) 请求方法是以下三种方法之一:

  • HEAD
  • GET
  • POST

(2)HTTP的头信息不超出以下几种字段:

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

凡是不同时满足上面两个条件,就属于非简单请求。

3.3.2 简单请求

如果是简单请求的话,会自动在头信息之中,添加一个Origin字段

GET /cors HTTP/1.1
Origin: http://api.bob.com 
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

这个Origin对应服务器端的Access-Control-Allow-Origin设置,所以一般来说需要在服务器端加上这个Access-Control-Allow-Origin 指定域名|*

3.3.3 非简单请求

如果是非简单请求的话,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。

浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。

需要注意这里是会发送2次请求,第一次是预检请求,第二次才是真正的请求!

首先发出预检请求:

// 预检请求
OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0..

除了Origin字段,"预检"请求的头信息包括两个特殊字段。

(1)Access-Control-Request-Method

该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,上例是PUT。

(2)Access-Control-Request-Headers

该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段,上例是X-Custom-Header。

然后服务器收到"预检"请求以后:

检查了OriginAccess-Control-Request-MethodAccess-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。

// 预检请求的回应
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

最后一旦服务器通过了"预检"请求:

以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。

// 以后的请求,就像拿到了通行证之后,就不需要再做预检请求了。
PUT /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

详情参考这里http://www.ruanyifeng.com/blog/2016/04/cors.html


参考文档:

  • 前端解决跨域问题的8种方案
  • 浏览器同源政策及其规避方法
  • https://tonghuashuo.github.io/blog/jsonp.html
  • http://www.cnblogs.com/yuzhongwusan/archive/2012/12/11/2812849.html
  • http://www.cnblogs.com/dowinning/archive/2012/04/19/json-jsonp-jquery.html
  • https://segmentfault.com/a/1190000002438126

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

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

相关文章

URAL 1682 Crazy Professor (并查集)

【题目链接】 http://acm.timus.ru/problem.aspx?space1&num1682 【题目大意】 给出k&#xff0c;从1开始不断地加一并把这个数写在黑板上&#xff0c;如果写上的数字和之前的数字满足   (ab*b)%k0或者(ba*a)%k0就在他们之间连一条线&#xff0c;如果黑板上出现环就结束…

利用Python随机或暴力生成密码

""" Title: python 密码生成 Author: JackieZheng Date: 2022-04-09 12:47:33 LastEditTime: 2022-04-09 14:00:46 LastEditors: Please set LastEditors Description: FilePath: \\pythonCode\\python_pwd_generater.py """import itertools im…

EasyNetQ-用于使用 RabbitMQ 的 .NET API开源的工具库

Part1介绍EasyNetQ 的目标是提供一个库&#xff0c;用于在 .NET 中使用 RabbitMQ 尽可能简单。为了做到这一点&#xff0c;它通过强制执行一些简单的约定来以灵活性换取简单性。这些包括&#xff1a;消息应该由 .NET 类型表示。消息应按其 .NET 类型进行路由。这意味着消息是由…

python 中 __name__ 的使用

1. 如果模块是被导入&#xff0c;__name__的值为模块名字2. 如果模块是被直接执行&#xff0c;__name__的值为’__main__’Py1.py #&#xff01;/usr/bin/env python def test():print __name__ ,__name__ if __name__ __main__:test() Py2.py #&#xff01;/usr/bin/env pyt…

第6章 循环

6.1 range 函数用来创建一个数字列表&#xff0c;它的范围是从起始数字开始到结束数字之前 1 >>> for x in range(0,5): 2 print(Hello %s % x) 3 4 Hello 0 5 Hello 1 6 Hello 2 7 Hello 3 8 Hello 4 1 >>> print(list(range(10,20))) 2 [10, 11, 12, …

C# 实例解释面向对象编程中的依赖反转原则

在面向对象编程中&#xff0c;SOLID 是五个设计原则的首字母缩写&#xff0c;旨在使软件设计更易于理解、灵活和可维护。这些原则是由美国软件工程师和讲师罗伯特C马丁(Robert Cecil Martin)提出的许多原则的子集&#xff0c;在他2000年的论文《设计原则与设计模式》中首次提出…

Linux学习笔记之一————什么是Linux及其应用领域

1.1认识Linux 1&#xff09;什么是操作系统 2&#xff09;现实生活中的操作系统 win7 Mac Android iOS 3&#xff09; 操作系统的发展史 &#xff08;1&#xff09;Unix 1965年之前的时候&#xff0c;电脑并不像现在一样普遍&#xff0c;它可不是一般人能碰的起的&#xff0c;…

Flex中宽度计算

flex 有三个属性值&#xff0c;分别是 flex-grow&#xff0c; flex-shrink&#xff0c; flex-basis&#xff0c;默认值是 0 1 auto。 发现网上详细介绍他们的文章比较少&#xff0c; 今天就详细说说他们&#xff0c;先一个一个看。 flex-grow 定义项目的放大比例&#xff0c;默…

Lucene详解

一.lucene原理 Lucene 是apache软件基金会一个开放源代码的全文检索引擎工具包&#xff0c;是一个全文检索引擎的架构&#xff0c;提供了完整的查询引擎和索引引擎&#xff0c;部分文本分析引擎。它不是一个完整的搜索应用程序&#xff0c;而是为你的应用程序提供索引和搜索功能…

.NET 6.0中使用Identity框架实现JWT身份认证与授权

原文作者&#xff1a;Sarathlal Saseendran原文链接&#xff1a;https://www.c-sharpcorner.com/article/jwt-authentication-and-authorization-in-net-6-0-with-identity-framework/翻译&#xff1a;沙漠尽头的狼&#xff08;谷歌翻译加持&#xff09;介绍微软于 2021 年 11 …

adb devices 里面有很多 emulator-XXXX的解决方法

2019独角兽企业重金招聘Python工程师标准>>> adb kill-server 转载于:https://my.oschina.net/sfshine/blog/700354

MQ(Message Queue)简介

一、何为MQ&#xff1f; MQ全称为Message Queue, 消息队列&#xff08;MQ&#xff09;是一种应用程序对应用程序的通信方法。应用程序通过读写出入队列的消息&#xff08;针对应用程序的数据&#xff09;来通信&#xff0c;而无需专用连接来链接它们。消息传递指的是程序之间通…

【GlobalMapper精品教程】015:矢量面图层的创建及数字化操作

本文讲解在Globalmapper中文23.0中创建矢量面状数据(政区数据),并进行面状数据采集及编辑的详细操作流程,数据为配套案例数据包中的data015.rar。 参考阅读: ArcGIS实验教程——实验三:矢量数据采集与编辑(矢量化) 文章目录 一、认识工具条1. 数字化(创建)工具条2. 选…

Blazor University (39)JavaScript 互操作 —— 更新 document title

原文链接&#xff1a;https://blazor-university.com/javascript-interop/calling-javascript-from-dotnet/updating-the-document-title/更新 document title源代码[1]在创建 Blazor 布局[2]部分中&#xff0c;我们看到了 Blazor 应用程序如何存在于 HTML&#xff08;或 cshtm…

IIS 日志文件位置

IIS 6 Log files location IIS 6中日志文件的位置%windir%\System32\LogFilesIIS 7 Log files location IIS的日志文件的位置%SystemDrive%\inetpub\logs\LogFiles用户每打开一次网页&#xff0c;iis 都会记录用户IP、访问的网页地址、访问时间、访问状态等信息&#xff0c;这些…

APP测试流程和测试点

1 APP测试基本流程 1.1流程图 1.2测试周期 测试周期可按项目的开发周期来确定测试时间&#xff0c;一般测试时间为两三周&#xff08;即15个工作日&#xff09;&#xff0c;根据项目情况以及版本质量可适当缩短或延长测试时间。正式测试前先向主管确认项目排期。 1.3测试资源 测…

39所强基计划试点高校已全部公布招生简章

截至目前(4月8日下午) 39所强基计划试点高校 已全部公布招生简章 各高校招生要求是什么&#xff1f; 招生专业有哪些&#xff1f; 什么时候报名&#xff1f; 一起来看 北京大学 招生对象及报名条件 各省&#xff08;区、市&#xff09;符合2022年全国普通高等学校招生统…

【ArcGIS错误异常100问】之001:License服务无法启动权威解决办法

测试环境&#xff1a; 操作系统&#xff1a;Windows10ArcGIS版本&#xff1a;10.X结果&#xff1a;通过测试 文章目录1. 错误提示2. 问题分析3. 解决办法3.1 关闭Windows Defender3.2 关闭系统防火墙3.3 删除迈克菲&#xff08;McAfee&#xff09;杀毒软件3.4 在系统服务中启动…

Appium wait等待的三种方法

1、sleep()方法Thread.sleep&#xff08;60000&#xff09;强制等待60s2、隐式等待implicitlyWait()driver.manage().timeouts().implicitlyWait(30,TimeUnit.SECONDS);全局等待30s不管元素是否已经加载1) 当使用了隐式等待执行测试的时候&#xff0c;如果WebDriver没有在DOM中…

ASP.NET Core 技术内幕与项目实战读后感

前几天拿到了杨中科老师的新书《ASP.NET Core 技术内幕与项目实战》&#xff0c;迫不及待的“两”口气读完了。用一句话来总结&#xff0c;这是一本写给.NET开发者的非常实用的接地气的好书&#xff0c;感觉有必要自发为这本书宣传一波。杨老师在 .NET 开发者社区中的知名度非常…