1. 程式人生 > 其它 >[web dev server proxy] 在實踐中深入瞭解跨域 + devServer.proxy

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