[web dev server proxy] 在实践中深入了解跨域 + devServer.proxy
转载于: https://www.bianchengquan.com/article/144302.html
1. 背景
前段时间想在群里搞个钉钉机器人定时提醒自己写周报,然而又碰到了跨域的情况,代码大致如下:
-- index.js --
import axios from 'axios' function sxx () { axios.post('https://oapi.dingtalk.com/robot/send?XXXXXXXX', { msgtype: 'text', text: { content: '[sxx]: 我就是我, 是不一样的烟火' } }) } sxx ()
-- webpack.config.js --
const path = require('path') module.exports = { entry: './index.js', devServer: { host: '0.0.0.0', port: 8080 }, plugins: [ new HtmlWebpackPlugin() ], output: { filename: 'main.js', path: path.resolve(__dirname, './dist') } }
报错如下所示:
2. 回顾跨域的知识
对于跨域,MDN里的这篇文章:HTTP访问控制(CORS)讲的很清晰。截取里面非常经典的图片(见下图),我们在domain-a.com网站下,访问domain-b.com的资源(png图片和eot的字体),这两个资源就会跨域,此时浏览器可能会阻止这两个资源请求的发送,也可能是跨站请求正常发送,但返回结果被浏览器拦截,具体要看浏览器的实现。
那么,对应于我们本例中的跨域情况,我们是在0.0.0.0:8080下,访问oapi.dingtalk.com的资源,由于该请求跨域,浏览器直接阻止该请求的发送
凭啥你说是阻止了请求的发送?
通过charles可以看到post请求https://oapi.dingtalk.com/robot/send
压根都没有发送,只是一个connect请求(况且若请求真的发送了,群里就会有钉钉机器人的消息了,返回的结果对我来说并没用)
3. 更改origin是否可以处理跨域
处理跨域常见的就是cors,服务器端设置Access-Control-Allow-Origin即可,可咱总不能让钉钉来添加吧?从跨域的原理看来,浏览器就是通过判断请求头中的origin结合请求的url来判断是否跨域的,那咱是不是可以更改origin来骗过浏览器?此时,咱们也有必要再了解下请求头中的host,origin字段:HTTP headers 之 host/referer/origin。于是,代码更改如下: -- index.js --
import axios from 'axios' function sxx () { axios.post('https://oapi.dingtalk.com/robot/send?XXXXXXXX', { msgtype: 'text', text: { content: '[sxx]: 我就是我, 是不一样的烟火' } }, { headers: {'Origin': 'oapi.dingtalk.com'} }) } sxx ()
然而,直接报错了...主要看第一个报错:Refused to set unsafe header "Origin"
搜索下stack overflow大佬们对于该报错的解释,其中的第一条留言就很到位,出于安全性的考虑,header中的origin是人家浏览器设置的,咱们无法更改它(想想也是,要是人人都可以更改origin,浏览器的同源策略岂不形同虚设)
4. webpack的devServer.proxy处理了跨域
之前看到慕课网里dell老师讲的devServer.proxy可以代理开发环境中的url,尝试了下竟然绕过了跨域,请求成功了,代码更改如下:
-- index.js --
import axios from 'axios' function sxx () { axios.post('/robot/send?XXXXXXXX', { msgtype: 'text', text: { content: '[sxx]: 我就是我, 是不一样的烟火' } }) } sxx ()
-- webpack.config.js --
const path = require('path') module.exports = { entry: './index.js', devServer: { host: '0.0.0.0', port: 8080, proxy: { '/robot': { target: 'https://oapi.dingtalk.com', secure: false, // 协议是https的时候必须要写 changeOrigin: true } } }, // ···省略了···太懒··· }
重点:添了changeOrigin: true
后才可跨域,否则还是不行。那么重点就是理解为啥加上changeOrigin
即可跨域
5. changeOrigin如何解决跨域
dell老师在视频里说changeOrigin
字段是为了防止网站被爬数据,会验证请求的origin,若origin不是本网站的,则请求无法获得结果,看起来像是改变了header里的origin,但是刚不是尝试了咱们是无法更改origin的么?
查看文档中关于changeOrigin
字段的描述:
changeOrigin: change the origin of the host header to the target url
还是不太明白,然后查看源码(在http-proxy/common.js中):
从源码里可以清晰的看到,设置了changeOrigin
只是更改了request请求中的host,并不是origin,那么更加奇怪了,到底是如何绕过跨域的呢?
其实,devServer中的proxy就相当于charles进行url的代理,在sxx()
执行后发送的请求是http://0.0.0.0:8080/robot/send?XXXXXXXX
,我们是在0.0.0.0:8080下,当然不会限制这样的请求的发送,然后devServer的proxy通过配置将host更改为oapi.dingtalk.com
,该请求就能正常进行,大致情况如下图所示:
6. charles模拟
为验证该想法,使用charles替换devServer.proxy进行url的代理,对于http://0.0.0.0:8080/robot/send?XXXXXXXX
进行Breakpoint,更改该请求的host。 但是,又报错啦...如下图
查询stack overflow中‘invalid Host header’,找到解决方法:只要在devServer中加个disableHostCheck: true
即可。