一个老课题:跨域和反向代理

329 阅读6分钟

跨域,老课题了!看到这个课题的感觉就像看到扒马褂说道大白马淹死在茶缸里面一样,蹄至背高八尺,头至尾长丈二张口就来,但是老话归老话,该聊还是要聊一聊。

跨域,本质上是为了保护用户的个人隐私而引入的一项保护性的政策,简单将就是如果将网址分为两个部分,一个是斜杠之前的部分,一个是斜杠之后的部分,那么斜杠之前的部分只要有一点不一样,就是跨域。

例如www.baidu.com,这个基本上是最常用的网站了,以今天的百度首页新闻为例,当我打开百度新闻的时候,我的网址就变成了

www.baidu.com/s?cl=3&tn=b…

其中com后面的那个/就是我说的斜杠,说的比较通俗,毕竟个人能力有限。那么这个斜杠之前的就是浏览器同源政策所描述的地方了。具体讲就是协议域名和端口。https是协议,www.baidu.com是域名,端口在网址上一般不显示,不过在开发过程中我们用的大部分应该都有端口,例如当我使用http-server创建一个小型服务器的时候我的默认端口就是8080。这三个只要有一个不一样那就是跨域,即使后面对应的是同一个ip,因为同源政策是浏览器的规则,不是后台服务的规则。

那么说完如何判断跨域了,就聊一聊跨域在工作中的使用吧!

首先也是最常用的就是开发过程中。我想大部分人的前后端分离项目都不是在服务器上面开发的,也就是当我们进行项目开发的时候我们面临的就是每一个接口都是通过跨域的方式去调用的。

其次当我们使用一些第三方的埋点工具的时候,必然是处在一种跨域的环境下去保持数据统计的。

还有就是经典的多平台合作等等,我所处的公司规模较小,没有这种情况,就没办法引申的往下说了。

当然,说不说,跨域还是要解决的,解决跨域的文章有很多,一般会给我们列举出十种八中的方法,但是实际工作中前端用的比较多得也就是两种,一种是开发中常用的反向代理,另一种是上线之后用的jsonp。其他的例如iframe嵌套修改片段识别码(也就是网址中#后面的部分),或者修改window.name,或者修改document.domain让两个二级域名相同的页面通过domain变成同源页面,还有就是postmessage我都没有用过,因为真的是没机会用。公司规模不大,没有那么多域名用来让我操作。websocket是一个不错的方法,但是如果用户量大的话服务器会经受一些奇妙的压力。至于后端添加跨域,那个不在前端的考虑范畴内,这篇文章直说前端部分。

先说jsonp,毕竟标题里面带了反向代理,肯定反向代理要多说几句的。

jsonp,简单实用,对服务器和浏览器几乎没什么要求。方法也比较简单,就是通过添加script标签来获取后台数据,就像我们引入第三方的库一样。这里顺便说一下,当我们使用html标签的时候,一般是不存在跨域问题的,无论是img,还是video,都是可以获取非同源的数据的,但是我们依然要使用script,我曾经试了一下用img去获取js文件,大概是因为使用的img标签导致浏览器的解析模式不同,并未获取到js文件里面的数据。

接下来接着说jsonp,说起来我到现在也不知道这个应该怎么读,我一般是根据意思分为json和p去读,毕竟jsonp的全称是json with padding。通过添加script去获取接口的数据,数据获取到了,但是如果让浏览器使用数据,或者说让我们的方法去使用这个数据就是jsonp的核心了。

最简单的方法就是直接在接口里面添加window.data = ...,这种方法比较粗暴,而且需要通过get,set去设定一个固定的值,效率上不太高。

那么增加一点效率,将里面的数据通过一个方法fn(data)直接调用,并且在外面声明一个名为fn的函数,这样既可以获取数据,而且不会改变window上面的变量。这种方法虽然粗暴的程度低了一些,但是效率还是不太足够,毕竟fn的方法太过于固定。

那么我们将接口后面加一个参数callback=fn,这样的话方法名可以通过callback传入,有一定的灵活性。这就是目前最常用的跨域的方法。

但是这种方法有一个天然的缺点,只能使用get方法,无论是安全性还是请求数据上都是无法修改的不足。

说完了跨域,我们也该说反向代理了。

我更愿意把反向代理算作跨域以外的一种方法,毕竟反向代理不是浏览器能做到的功能。反向代理的本质是搭建一个与浏览器域名相同的一个服务器,通过这个服务器去访问非同源的接口服务器。

比起这么说我更喜欢举个例子。

本来这里放了一个举个栗子的图片,但是因为那个图片有点太大了,我就删掉了。

当我开发的时候,我的本地ip是192.168.1.111,这个时候我访问一个ip为172.1.149.1的接口是跨域的,但是我的页面的网址并不是这个ip,我就只能创建一个本地服务器,服务器的ip是192.168.1.111,端口是8080,我使用浏览器访问这个我的本地服务器,本地服务器去访问接口,接口返数据给本地服务器,本地服务器在将数据反馈给浏览器,这个时候浏览器就可以不跨域的获取到接口的数据。这就是反向代理。

当然,前端开发的过程中是不需要这么复杂的去开发一个代理服务器的,以vue为例,vue-cli的脚手架本身是有反向代理服务器的,只需要在vue.config.js文件中配置一下就可以了。

devServer: {    
    proxy: {        
        '/api': {            
            target: 'http://192.168.31.87:8081',            
            ws: true,            
            changeOrigin: true,            
            pathRewrite: {                
                '^/api': '/'            
            }        
        }    
    }
},

其中target后面对应的值是需要调用接口的协议、ip和端口。ws是代理websocket,changeOrigin是跨域访问,pathRewrite是路径重写,其中路径重写一定要修改。

这个反向代理的使用方法是当你的调用接口的时候将原来的ip改为/api开头,服务器检测到就会将这次请求通过反向代理的方式去请求,将/api改为目标ip。如果没有pathRewrite,那么接口地址后面就会紧跟着/api,而且是每个接口都会有。

这个图片就是pathRewrite添加与没添加的区别。