[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
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
即可。