前后分離 跨域問題

 框架教程     |      2023-06-16 17:44:45

問題:

has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.



解決辦法:

根據現象可以發現:

  • 先是一個預檢(preflight)請求
    請求頭:

OPTIONS /user/getxxxxxxxxxxxxxxxUser?tokenId=tokenId_590fddfe_eb95_46d1_a208_659e3a8d0a57 HTTP/1.1Accept: */*
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
Origin: http://xxx.yyy.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36
Sec-Fetch-Mode: cors

返回頭:

HTTP/1.1 307 Internal Redirect
Location: https://xxx.yyy.com/user/getxxxxxxxxxxxxxxxUser?tokenId=tokenId_590fddfe_eb95_46d1_a208_659e3a8d0a57
Non-Authoritative-Reason: HSTS
Access-Control-Allow-Origin: http://origin.zzz.com
Access-Control-Allow-Credentials: true

結論

先說結論:
-- 原因是瀏覽器在發送http請求時自動進行307內部跳轉,導致了跨域預檢失敗。
-- 為什么會307內部跳轉呢? 是因為瀏覽器采用HSTS(HTTP Strcit-Transport-Securit)策略,將所有非http的請求內部跳轉成https。
-- 為什么瀏覽器認為這個域名需要采用HSTS策略呢?因為服務端/nginx返回的http請求header里配置了Strict-Transport-Security(嚴格傳輸安全)選項。

原理

跨域(CORS)問題

  • 什么是跨域問題?
    -- 出于安全性,瀏覽器限制腳本內發起的跨源HTTP請求。瀏覽器限定了不能任意訪問非同源的資源,這意味著使用這些API(例如:XMLHttpRequest和Fetch API)的Web應用程序只能從加載應用程序的同一個域請求HTTP資源,除非響應報文包含了正確CORS響應頭。
    -- CORS(Cross-origin resource sharing) -- 跨源資源共享

  • 簡單請求和復雜請求
    -- 瀏覽器將CORS請求分成兩類:簡單請求(simple request)和非簡單請求(not-so-simple request)。如果是簡單請求直接訪問,如果是非簡單請求需要先發預檢請求preflight。
    -- 如何區分簡單請求和負責請求?
    滿足下面條件的即為簡單請求,否則為復雜請求
    (這里插一句:曾經有一段代碼,雖然是get請求,但是因為Content-Type 寫了json 導致了也發出了預檢請求,我們排查了半天才明白)

1. 請求頭方法必須是 HEAD/GET/POST2. HTTP的頭信息不超出以下幾種字段:
   Accept
   Accept-Language
   Content-Language
   Last-Event-ID
   Content-Type:只限于三個值application/x-www-form-urlencoded、multipart/form-data、 
   text/plain
  • 簡單請求
    如果是簡單請求,request請求header里需要有一個origin 字段,response請求返回的header里需要標識Access-Control-Allow-Origin 表示允許此源的網站請求。如果沒有返回對應的,瀏覽器知道出錯了,會拋出一個錯誤。

  • 復雜請求
    -- 預檢:復雜請求需要先發起一個預檢請求(preflight),如果服務器返回的response中有對應的Access-Control-Allow-Origin頭,表示預檢通過。然后才能進入下一步
    -- 正式請求:一旦服務器通過了"預檢"請求,以后每次瀏覽器正常的CORS請求,就都跟簡單請求一樣,會有一個Origin頭信息字段。服務器的回應,也都會有一個Access-Control-Allow-Origin頭信息字段。

307跳轉問題

現代瀏覽器和服務器都開始支持 HSTS(HTTP Strict Transport Security) 功能,即自動將不安全的 HTTP 請求使用 307 Internal Redirect 跳轉到 HTTPS 請求。這是由Chrome內部HSTS緩存導致的。Chrome 會自動記住每個域的 HSTS 設置,也就是說HSTS只要在理論上的第一次暴露后,后來就不經網頁服務器返回,瀏覽器會查詢本地數據,直接偽造 HSTS 307 跳轉到安全的 HTTPS,以此來加強網絡訪問的安全性。

服務器設置問題

問題是,是誰告訴瀏覽器這么做的呢?
原因是在nginx配置中有這么一段:

add_header Strict-Transport-Security max-age=31622400;

瀏覽器接到這樣的回復頭:

HTTP/1.1 200 OKServer: 5.62Date: Tue, 14 Sep 2021 08:10:07 GMTContent-Length: 0Connection: keep-aliveStrict-Transport-Security: max-age=31622400

這里意思是通知瀏覽器,訪問的這個域名需要采用HSTS策略,并且過期時間是31622400秒(一年),在這一年中都需要使用HSTS策略,意味著發起http請求時,瀏覽器都會強行進行307內部跳轉。

然后因為307跳轉被cors預檢請求認為是不合法的,故此預檢失?。?/p>

has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.

解決方案

  1. 臨時解決(以chrome為例) — 打開:chrome://net-internals/#hsts
    找到: "Query HSTS/PKP domain"  找到報錯的域名 瀏覽器有沒有記錄

    image.png


    然后找到:"Delete domain security policies" 刪掉記錄。


    image.png


    立刻就能訪問,但是這種方法當用戶再次訪問了https之后,仍然會報錯

  2. nginx端解決:

將:          add_header Strict-Transport-Security max-age=31622400
改為:  add_header Strict-Transport-Security max-age=0;

  1. 前端解決:
    所有配置了https的域名都采用 https鏈接訪問保持一致性

FAQ:

  1. 為什么清空緩存就能暫時解決?
    -- 因為清除了瀏覽器中關于此域名的HSTS的記錄,在用戶訪問https之前不會再次出現問題

  2. 為什么是偶發現象?
    -- 因為大部分請求是簡單請求,例如get請求。所以直接跨域的問題就通過了

  3. 為什么有的域名nginx也配置了add_header Strict-Transport-Security max-age=31622400 但從來沒有跨域問題?
    -- 可能是域名的證書并沒有生效,導致瀏覽器的沒有留下HSTS記錄。



作者:leslieluyu
鏈接:https://www.jianshu.com/p/08fefb25ed17
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。




以上就是“前后分離 跨域問題”的詳細內容,更多請關注木子天禾科技其它相關文章!