1. 程式人生 > >對CROS OPTIONS預檢請求的一些思考

對CROS OPTIONS預檢請求的一些思考

前後端分離模大勢所趨,跨域問題更是老生常談。 問題背景: > 瀏覽器最基本的安全規範-**同源策略**。所謂同源是指域名、協議、埠相同。不同源的瀏覽器指令碼(javascript、ActionScript、canvas)在沒有明確授權的情況下,不能讀寫對方的資源。 CORS就是w3c和瀏覽器廠商為解決跨域資源共享問題而推出的標準方案:它允許瀏覽器向跨源伺服器發出指令碼請求,CORS需要瀏覽器和伺服器同時支援,它的通訊過程都是瀏覽器自動完成的,不需要使用者參與。 瀏覽機器一旦發現跨域,就會自動新增一些附加的頭資訊,有時還會多出一次附加的請求,但使用者不會察覺,伺服器響應特定標頭Access-Control-,體現跨源訪問的授權。 --- 今天我主要想要聊一聊CORS中的**預檢**請求 當前端使用指令碼請求一個跨域資源時,如果是非簡單請求(下文會解釋),瀏覽器會自動幫你先發出一個`OPTIONS`查詢請求,稱為預檢(`cors-preflight-request`),作用是詢問伺服器,當前網頁所在的域名是否在伺服器的許可名單之中,以及可以使用那些HTTP動詞和頭資訊欄位。 只有得到肯定答覆,瀏覽器才會發生正式的XHR請求。 ![](https://img2020.cnblogs.com/blog/587720/202101/587720-20210103160610413-1768220222.jpg) 該請求header中會包含以下兩個欄位: - Access-Control-Request-Method: 該欄位的值對應當前請求型別,例如 GET、POST、PUT等等。瀏覽器會自動處理。 - Access-Control-Request-Headers: 該欄位的值對應當前請求可能會攜帶的額外的自定義header欄位名,多個欄位用逗號分割。瀏覽器會自動處理,將請求中非簡單的header欄位全部列出來,例如標識請求流水的x-request-id,用於Auth鑑權的Authorization 欄位。 對於 OPTIONS 請求,按照規範實現的服務端會響應一組HTTP header,但不會返回任何實體內容。如果服務端支援該跨域請求,建議返回 204狀態碼(返回200也可以);如果不支援,建議返回403狀態碼(返回404或其他錯誤狀態碼也可以)。 響應的header可以包含以下欄位: - Access-Control-Allow-Origin: 允許哪些域被允許跨域,例如 http://qq.com 或 https://qq.com,或者設定為 * ,即允許所有域訪問 - Access-Control-Allow-Credentials: 是否攜帶票據訪問(對應fetch方法中credentials),當該值為true時,Access-Control-Allow-Origin 不允許設定為* - Access-Control-Allow-Methods: 標識該資源支援哪些方法,例如:POST, GET, PUT, DELETE - Access-Control-Allow-Headers: 標識允許哪些額外的自定義 header 欄位和非簡單值的欄位 - Access-Control-Max-Age: 表示可以快取Access-Control-Allow-Methods和Access-Control-Allow-Headers提供的資訊多長時間,單位秒,由服務端和瀏覽器預設值共同決定。 - Access-Control-Expose-Headers: 通過該欄位指出哪些額外的 header 可以被支援。 由此可見,當觸發預檢時,一次AJAX請求會消耗掉兩個TTL,嚴重影響效能。 那麼如何節省掉 OPTIONS 請求來提升效能呢?從上文可以看出,有兩個方案: 1. 發出簡單請求 只要同時滿足一下兩個條件,就屬於簡單請求 (1)使用下列方法之一: - head - get - post (2)請求的Heder是 - Accept - Accept-Language - Content-Language - Content-Type: 只限於三個值:application/x-www-form-urlencoded、multipart/form-data、text/plain 不同時滿足上面的兩個條件,就屬於非簡單請求。 很明顯,我們常見的Post請求、媒體型別Content-Type=application/json也屬於非簡單請求,也會觸發預檢請求。如果不方便改造為簡單請求,只有使用方案2了。 2. 伺服器端設定`Access-Control-Max-Age`欄位 當第一次請求該URL時會發出`OPTIONS`請求,瀏覽器會根據返回的`Access-Control-Max-Age`欄位快取該請求的`OPTIONS預檢請求`的響應結果。在快取有效期內,該資源的請求(URL和header欄位都相同的情況下)不會再觸發預檢。(chrome 開啟控制檯可以看到,當伺服器響應`Access-Control-Max-Age` 時只有第一次請求會有預檢,後面不會了。注意要開啟快取,去掉`disable cache`勾選) > 但是要注意的是,該快取只針對這一個請求 URL 和相同的 header,無法針對整個域或者模糊匹配URL做快取。(當然也可以考慮封裝一下,固定一個介面地址,傳不同的body內容)。 下面是Abp vNext配置CROS的示例: ``` private void ConfigureCors(ServiceConfigurationContext context, IConfiguration configuration) { context.Services.AddCors(options => { // 無阻塞跨域 options.AddPolicy(DefaultCorsPolicyName, builder => { builder.SetIsOriginAllowed(_ => true) .AllowCredentials() .AllowAnyHeader() .WithMethods(HttpMethods.Get, HttpMethods.Post, HttpMethods.Put, HttpMethods.Delete) .SetPreflightMaxAge(TimeSpan.FromHours(24)); }); }); } ``` - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-