CORS 理解(不要那麼多術語)

pelemy發表於2018-12-22

摘要

談到跨域,不論前端還是後端,多少有點談虎色變,面試中也常會問到這些問題,瀏覽器和伺服器端到底怎麼做才能跨域,他們都做了什麼?

同源 vs 跨域

同源,字面意義是相同的源頭,即同一個web伺服器(比如tomcat啟動的一個例項),好比一個家庭;跨域就是從一個web伺服器向另一個伺服器傳送或獲取資料,好比去鄰居家拿東西。之所以要做跨域限制,是為了安全,你不能從鄰居家直接拿吧,總得問過人家。

那麼怎麼來判斷同源?就看一個web伺服器的根本三要素:協議、web伺服器域名、埠號。

比如:當前頁面的地址是http://myhost.com:8080/index,源就是http://myhost.com:8080,當新請求的協議、域名、埠號與之完全一致即是同源,否則是跨域。

對於下面傳送的請求,瀏覽器的判決如下:

  • http://www.myhost.com:8080/users 跨域 ->
    域名必須完全一致
  • https://myhost.com:8080/users 跨域 ->
    協議必須一致
  • https://myhost.com:9000/users 跨域 ->
    埠必須一致
  • https://myhost.com/users 跨域 ->
    埠必須一致(沒有埠也是不一致)
  • https://myhost.com:8080/profile/users 同源

跨域時的動作

瀏覽器端

假設我們點選一個按鈕去獲取資料,獲取資料的請求準備從瀏覽器發出,這時瀏覽器先會檢測這條請求是同源還是跨域,也就是與按鈕所在頁面的地址是同源還是跨域,如果是同源,好說,直接傳送出去;如果是跨域的請求,那就得hold住先,瀏覽器會在請求的http header中加上一個Origin欄位,標明這個請求是從哪裡發出來的,例如: Origin:http://neighbour.com:9000,這樣伺服器端好辨識是自己家人來取東西,還是隔壁老王來借東西了。

那些做檢測、加header欄位等事情全是瀏覽器做,對於前端開發者來說,什麼事都不用幹,ajax請求平時怎麼傳送,跨域時還怎麼傳送。可見,跨域對於前端沒影響,關鍵在於伺服器端。

伺服器端

每個web伺服器就像一個家庭。好鄰居要吃螃蟹來借點醋,這沒問題;壞鄰居家有醋要來借點螃蟹,這不幹。

伺服器收到請求會給與響應,響應的header裡寫明跨域的配置資訊,告訴瀏覽器,它允許哪些域名發來的請求訪問,哪些method可以執行。瀏覽器收到響應後自動判斷能不能真正執行請求。

假設伺服器域名是http://rich.com:8080,它收到了一個從http://neighbour.com:9000發來的請求http://rich.com:9000/borrow-vinegar,伺服器響應它,在響應中寫明本伺服器支援哪些域名可以訪問,哪些method可以執行,瀏覽器收到後做匹配,再判定。

那麼,伺服器有哪些判定的規則呢?

是否允許跨域的判定

一個支援CORS的web伺服器,有如下的判定欄位,他們會在響應的header中寫明

  • Access-Control-Allow-Origin:允許跨域的Origin列表
  • Access-Control-Allow-Methods:允許跨域的方法列表
  • Access-Control-Allow-Headers:允許跨域的Header列表
  • Access-Control-Expose-Headers:允許暴露給JavaScript程式碼的Header列表
  • Access-Control-Max-Age:最大的瀏覽器快取時間,單位為s

其中Access-Control-Allow-Origin(訪問控制之允許的源),在響應的http header中必須有的,表示允許訪問本伺服器的源頭Origin(域名),可以是特定的域名列表,用逗號分隔,也可以是萬用字元 *,表示支援任意域名的訪問。

除了限定源頭Origin,還會限制請求的方法MethodHeader

如,如果伺服器設定Access-Control-Allow-Methods:GET,那麼跨域的POST請求無法在這個伺服器執行。

總流程

  • 頁面傳送請求
  • 瀏覽器根據同源策略做出判定,如果是同源請求,直接傳送出去;如果是跨域請求,在HTTP HEADER加上Origin欄位,或是先傳送一次預檢請求(preflight)。
  • 伺服器接收請求,根據自身跨域的配置(如允許哪些域名,什麼樣的Method訪問),返回檔案頭。若未配置過任何允許跨域,則檔案頭裡不包含Access-Control-Allow-origin欄位,若配置過域名,則返回Access-Control-Allow-origin+ 對應配置規則裡的域名的方式。
  • 瀏覽器接收到響應,根據響應頭裡的`Access-Control-Allow-origin欄位做匹配,如果沒有這個欄位,說明不匹配;如果有,將欄位內容和當前域名做比對。如匹配,則可以傳送請求。

跨域的請求形式

跨域的請求分兩種,一種是簡單請求,一種是非簡單請求(廢話)。

  • 簡單請求,方法僅限於 HEAD,GET或POST,且Header的欄位不超過以下欄位:
  • HeadeAccept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type:只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain (沒有application/json, 說明如果傳送JSON格式的body請求資料是一個非簡單請求)
  • 非簡單請求就是其他請求

簡單請求瀏覽器會直接在請求的Header加上Origin欄位再傳送;非簡單請求瀏覽器則會先傳送一次預檢請求,根據預檢請求的結果,決定是否正式傳送請求。

詳情可以訪問阮一峰的日誌:[跨域資源共享 CORS 詳解][1][1]: www.ruanyifeng.com/blog/2016/0…

來源:https://juejin.im/post/5c1df8d4f265da615d72a1e4

相關文章