天天看點

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

摘要

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

同源 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

,還會限制請求的方法

Method

Header

如,如果伺服器設定

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 詳解

來源:

https://segmentfault.com/a/1190000017481505

繼續閱讀