預檢請求 OPTIONS


CORS 中的預檢請求

在 CORS 機制中,客戶端將請求分為了兩種:簡單請求非簡單請求;當請求為非簡單請求時,就會觸發瀏覽器發送預檢請求,這是瀏覽器的行為。

預檢請求會向服務器確認跨域是否允許,服務返回的響應頭里有對應字段Access-Control-Allow-Origin來給瀏覽器判斷:如果允許,瀏覽器緊接着發送實際請求;不允許,報錯並禁止客戶端腳本讀取響應相關的任何東西。

所以,一個 POST 請求並且請求頭添加了Content-Type: application/json ,瀏覽器判定為非簡單請求,自己先發一個 OPTIONS 給服務器獲取做跨域判定,獲取響應后瀏覽器發現可以跨域,接着就發送真實的 POST。

這里就解答兩個問題了,接下來就是為什么預檢請求選擇了 OPTIONS 呢?

來看預檢請求的流程,如果是一個跨域請求,瀏覽器會自動給該請求帶上 Origin 頭部,標明當前請求的來源域;服務器判斷這個請求是否允許跨域,就會在返回時,選擇是否帶上 Access-Control-Allow-Origin 頭部,最后,瀏覽器判斷 Access-Control-Allow-Origin 就知道,后續請求是否發送。

這個流程中,對預檢請求方法的要求:

  1. 不需要帶請求體,服務器判斷的依據在 Request header 中;
  2. 服務器返回不需要響應體,瀏覽器判斷的依據在 Response header 中;
  3. 請求不會去修改服務器資源,要是一個安全的請求;
  4. 瀏覽器默認不會緩存,需要每次發送跨域驗證;

再看看 OPTIONS 的定義,會有一種量身定做的感覺。

但是,這里還是有問題:

  1. 既然服務端做了請求限制,而且瀏覽器判斷跨域只和 Access-Control-Allow-Origin 有關,預檢請求是否有點多余?
  2. 原生 form 表單可以提交 POST 請求,而且為一個簡單請求,很可能修改服務端數據,僅僅依靠 CORS 機制也不安全。

預檢請求的意義

瀏覽器為了安全的數據傳輸,提出了 CORS 機制,它更像一種授權機制,需要瀏覽器和服務器共同配合實現,對於沒有實現此機制的客戶端,比如 curl,是不受限制的:

上圖中,服務器實現很簡單,僅僅返回預先定義好的數據,curl 的返回頭中也沒有和Access-Control-*相關字段,但是返回體中,我們能夠看到返回數據,想必解析出來也並不困難。同樣的請求在瀏覽器中就會報錯了:

從這里可以看出,在 CORS 機制中,默認服務器為禁止跨域,服務器啥也不做就能禁止瀏覽器跨域了;但是,實際中能發請求的客戶端很多,每個請求的目的很復雜,對於那些真正要禁止跨域傳輸的服務自然有一套處理邏輯,這些邏輯很可能是復雜和高消耗的。

如果類似瀏覽器這種,包含 CORS 機制的客戶端發送的請求,每次都要經過一個復雜邏輯才能知道自己是否跨域,服務器的壓力和用戶體驗是不理想的,那么預檢請求就孕育而生:發送實際請求前,先發送預檢請求詢問服務器是否允許跨域,不允許就不發送實際請求,服務器只需要對預檢請求進行跨域處理。

這樣來看,在 CORS 機制中,發送預檢請求是一種保護機制,保護資源不被未授權的請求修改。和授權服務很像,預檢請求通過了,瀏覽器后續對同一服務的請求,不需要做跨域詢問,服務端不想支持跨域訪問,啥也不用做。

表單請求

原生 form 表單請求,和 action,method,enctype 屬性有關(回顧這三個重要屬性)。form 表單提交后,會自動跳轉頁面到 action 所指向的 URL 來獲取結果,最后變成同域,在沒有 AJAX 技術的時候,我們發 POST 一般會提交到當前 URL,后端響應 POST 請求,處理之后,又將當前頁面返回瀏覽器重新渲染,這也是每次提交表單會刷新頁面的原因。

而 method 和 enctype 可以的取值,也出現在了簡單請求的定義里面,所以提交傳統表單請求,是不會引發預檢請求,如果服務端對操作數據更改的接口,不做請求來源限制,即便是不允許跨域,變化依然會在服務端發生。

最后,HTTP 服務是無狀態的哦!

無狀態服務器,是指一種把每個請求,作為與之前任何請求都無關的,獨立的服務器。

即便通過了預檢請求的檢查,瀏覽器的后續請求,服務端只會響應當前請求;所以,可能出現預檢請求沒問題,后續的實際請求出現跨域:

所以,對於無狀態服務,真想限制跨域資源傳輸,僅僅依賴 CORS 機制是做不到的, 除了瀏覽器這種客戶端遵守了並實現了 CORS,世界上這么多客戶端是不遵守的:curl(可以回顧之前的示例圖)、wget、server proxy,還有各種爬蟲腳本,postman 這種 API 測試工具,可能都沒實現,都能正常發送請求和獲取數據。

想一想,這個請求可能是 CRUD 中的任何一種,成功執行服務端邏輯並返回 2xx,這時,CORS 驗證機制像是馬后炮。假如服務器不想支持跨域訪問,還是得在響應處理的時候,去實現過濾機制防止資源被非法獲取或修改。


 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM