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

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

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

jsonp(只支持 get)

反向代理

CORS

document.domain + iframe 跨域

window.name + iframe 跨域

window.postMessage

location.hash + iframe

web sockets

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

CORS 是跨源资源分享(Cross-Origin Resource Sharing)的缩写。它是 W3C 标准,是跨源 AJAX 请求的根本解决方法。

优点是:正统,符合标准,

缺点是:需要服务器端配合,比较麻烦。

JSONP 的核心则是动态添加 标签来调用服务器提供的 js 脚本。

优点是:对旧式浏览器支持较好,

缺点 1: 只支持 get 请求。

缺点 2:有安全问题 (请求代码中可能存在安全隐患)。

缺点 3:要确定 jsonp 请求是否失败并不容易。

反向代理都能够兼容以上的确定,但是仅仅作为前端开发模式的时候使用,在正式上线环境较少用到。

一般开发环境的域名跟线上环境不一样才需要这样处理。

如果线上环境太复杂,使用反向代理实现跨域的将会变得很麻烦,那么这时候会需要采用 jsonp 或者 CORS 来处理。

这里主要说明这三种方式。其他方式暂不说明。

一、什么是跨域问题

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

提示的错误大致如下:

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

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

换句话来说,就是跨越了浏览器的同源策略限制的时候,就会触发了我们所说的 “跨域” 问题。

2.1 什么是同源策略

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

协议相同

域名相同

端口相同

举例来说, 这个网址,

总的来说,只要不是三者同时相同,那么就不是同源,那么就会触发同源策略限制。

2.2 同源策略限制了什么

限制了:

Cookie、LocalStorage 和 IndexDB 无法读取

DOM 和 JS 对象无法获取

Ajax 请求发送不出去

这就是我们平常所说的 “跨域问题”。

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

三、解决跨域问题

3.1 使用反向代理方式3.1.1 什么是反向代理?

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

正向代理(Forward Proxy),通常都被简称为代理,就是在用户无法正常访问外部资源,比方说受到 GFW 的影响无法访问 twitter 的时候,我们可以通过代理的方式,让用户绕过防火墙,从而连接到目标网络或者服务。

反向代理(Reverse Proxy)是指以代理服务器来接受 Internet 上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给 Internet 请求连接的客户端,此时,代理服务器对外就表现为一个服务器。

那么可以利用反向代理的原理,我们通过一个中间代理服务器(反向代理服务器),将客户端网络请求的一些 host,domain,port 和协议等东西进行改写,使其模拟为可以访问目标服务器的请求,模拟成不触犯同源策略的请求去请求目标服务器。

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

前端 ajax 请求的是本地反向代理服务器

本地反向代理服务器接收到后:

修改请求的 http-header 信息,例如 referer,host,端口等

修改后将请求发送到实际的服务器

实际的服务器会以为是同源(参考同源策略)的请求而作出处理

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

这段代码的执行原理是:

node js 作为反向代理服务器,然后在它上面使用 express 实现路由功能,

在 nodejs 里面加入一条负责源端请求的路由映射,将它映射到目标服务器的 api 接口上,并且在这条路由里面将实现请求的改写,模拟目标服务器 api 接口的同源策略所需的要求。

源端会先请求 nodejs 反向代理服务器的之前设置的那条路由,会将参数传给他,然后 nodejs 反向代理会将它的请求进行改写,然后转发到目标服务器。

3.2 使用 JSONP 方式

3.2.1 什么是 JSONP

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

AJAX 无法跨域是受到 “同源政策” 的限制,但是带有 src 属性的标签(例如 )是不受该政策限制的,因此我们可以通过向页面中动态添加 标签来完成对跨域资源的访问,这也是 JSONP 方案最核心的原理,换句话理解,就是利用了前端请求静态资源的时候不存在跨域问题这个思路。

JSONP 只能用 get 方式。

JSONP(JSON Padding) 也叫填充式 JSON,他是 json 的一种使用方式,它允许用户传递一个 callback 参数给服务端,然后服务端返回数据时会将这个 callback 参数作为函数名来包裹住 JSON 数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。

3.2.1 如何使用 JSONP 来解决跨域问题:

简单一点的例子:

通过不受同源策略限制的标签,例如 script,将一段 js 代码间接地从外部引入。通过 script 标签向目标源发起一个 GET 请求,服务器根据请求的参数返回包含 js 的代码。

先在本地定义了一个函数,这是用来处理来自服务器上数据的函数,下面用一个 script 标签,并且向服务器发起了一个 GET 请求,并且指定了处理数据的回调函数,即上方的 getData,服务器收到请求后返回了 ,即使一段 js 代码,将数据传入到回调函数中处理,这样便完成了跨域。

参考:https://segmentfault.com/a/1190000004761698

复杂一点的例子:

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

客户端和服务器端约定一个参数名是代表 jsonp 请求的,例如约定 callback 这个参数名。

然后服务器端准备好针对之前约定的 callback 参数请求的 javascript 文件,这个文件里面要有一个函数名,要跟客户端请求的时候的函数名要保持一致。(如下面例子: )

然后客户端注册一个本地运行的函数, 并且函数的名字要跟去请求服务器进行 callback 回调的函数的名字要一致。(如下面例子:foo 函数跟请求时候 的名字是一致的)

然后客户端对服务器端进行 请求。

服务器端返回刚才配置好的 js 文件( )到客户端

客户端浏览器,解析 script 标签,并执行返回的 javascript 文件,此时数据作为参数,传入到了客户端预先定义好的 callback 函数里。

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

服务器端文件

客户端文件

3.3 CORS 方式

CORS 是一个 W3C 标准,全称是 "跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出 请求,从而克服了 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 字段,Origin 字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。

如果 Origin 指定的源,不在许可范围内,服务器会返回一个正常的 HTTP 回应。浏览器发现,这个回应的头信息没有包含 字段,就知道出错了,从而抛出一个错误,被 的 回调函数捕获。注意,这种错误无法通过状态码识别,因为 HTTP 回应的状态码有可能是 200。

这个 Origin 对应服务器端的 设置,所以一般来说需要在服务器端加上这个 即可,类似这种:

3.3.3 非简单请求

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

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

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

首先发出预检请求:

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

(1)

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

(2)

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

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

检查了 、 和 字段以后,确认允许跨源请求,就可以做出回应。

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

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

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

总的来说,只需要知道 2 个地方即可,其他的可以触类旁通:

CORS 需要服务器那边加一个 的处理,目的是为了处理请求的来源判别。

CORS 对于非简单请求会增加一次 OPTIONS 的请求。

参考文档:

前端解决跨域问题的 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

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180801A1JTFU00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券