iOS防DNS汙染方案調研—302等URL重定向業務場景

stevechen1010發表於2017-09-13

概述

302等 URL 重定向業務場景需要解決的問題:

302 等重定向狀態碼,如何正確執行跳轉邏輯,要求跳轉後,依然需要執行 IP 直連邏輯,多次 302,也能覆蓋到。

302等 URL 重定向業務場景問題主要集中在 POST 請求上,解決方案的方向大致有幾種:

  • 將請求方式統一替換為 GET
  • 解決 POST 請求時的重定向問題

將 URL 統一替換為 GET,這種方案在客戶端這邊是成本最低的,如果團隊中達成一致是最好的。不過限制也是顯而易見的。那麼我們就著重討論下如何解決 POST 請求時的重定向問題。

POST 請求的重定向問題

對於 GET 請求,重定向問題較為簡單,我們著重討論下 POST 請求的重定向問題,看下不同狀態碼下的響應方式。

下面介紹下重定向的型別以及解釋:

重定向的型別 對應協議 解釋
300 Multiple Choices HTTP 1.0 可選重定向,表示客戶請求的資源已經被轉向到另外的地址了,但是沒有說明是否是永久重定向還是臨時重定向。
301 Moved Permancently HTTP 1.0 永久重定向,同上,但是這個狀態會告知客戶請求的資源已經永久性的存在在新的重定向的 URL 上。
302 Moved Temporarily HTTP 1.0 臨時重定向,在 HTTP1.1 中狀態描述是 Found,這個和 300 一樣,但是說明請求的資源臨時被轉移到新的 URL 上,在以後可能會再次變動或者此 URL 會正常請求客戶的連線。
303 See Other HTTP 1.1 類似於 301/302,不同之處在於,如果原來的請求是 POST,Location 頭指定的重定向目標文件應該通過 GET 提取(HTTP 1.1新)。
304 Not Modified HTTP 1.0 並不真的是重定向 – 它用來響應條件 GET 請求,避免下載已經存在於瀏覽器快取中的資料。
305 Use Proxy HTTP 1.0 客戶請求的文件應該通過 Location 頭所指明的代理伺服器提取(HTTP 1.1新)。
306 HTTP 1.0 已廢棄,不再使用
307 Temporary Redirect HTTP 1.1 和302(Found)相同。許多瀏覽器會錯誤地響應 302 應答進行重定向,即使原來的請求是 POST,即使它實際上只能在 POST 請求的應答是 303 時 才能重定向。由於這個原因,HTTP 1.1新增了 307,以便更加清楚地區分幾個狀態程式碼:當出現 303 應答時,瀏覽器可以跟隨重定向的 GET 和 POST 請求;如果是 307 應答,則瀏覽器只能跟隨對 GET 請求的重定向。(HTTP 1.1新)

因為常見的重定向為 301、302、303 307,所以下面重點說說這幾種重定向的處理方法。

HTTP1.0 文件中的 302(或301) 狀態碼,原則上是要被廢棄的,它在 HTTP1.1 被細分為了 303 和 307。不過 303 和 307 應用並不廣泛,現在很多公司對 302(或301) 處理實際上是 303。

總結起來就是:

協議 狀態碼 協議規定 實際情況
HTTP1.0 302(或301) 不建議使用 仍在大面積使用
HTTP1.1 303 + 307 舊有302(或301)被細分,並建議使用的新的狀態碼 應用面積較小

這些新舊協議的主要差別集中在 POST 請求的重定向處理上:

對於301、302的location中包含的重定向url,如果請求method不是GET或者HEAD,那麼瀏覽器是禁止自動重定向的,除非得到使用者的確認,因為POST、PUT等請求是非冥等的(也就是再次請求時伺服器的資源可能已經發生了變化)

另外注意307這種情況,表示的是 POST 不自動重定向為 GET ,需要詢問訪問當前 URL 的使用者,是否需要重定向,進行手動重定向。

目前瀏覽器大都還把302當作303處理了(注意,303是HTTP1.1才加進來的,其實從HTTP1.0進化到HTTP1.1,瀏覽器什麼都沒動),它們獲取到 HTTP 響應報文頭部的 Location 欄位資訊,併發起一個 GET 請求。

我們可以根據業務需要,對不同的狀態碼做處理,比如可以對303狀態碼做如下處理,

  • location 中包含重定向 URL 就重定向
  • 如果是 POST 請求修改為 GET 請求,並清空 body。
  • 清空 host 資訊
  • 重新傳送網路請求

程式碼示例:

   NSString *location = self.response.headerFields[@"Location"];
   if (location && location.length > 0) {
       NSURL *url = [[NSURL alloc] initWithString:location];
       NSMutableURLRequest *mRequest = [self.swizzleRequest mutableCopy];
       mRequest.URL = url;
       if ([[self.swizzleRequest.HTTPMethod lowercaseString] isEqualToString:@"post"]) {
           // POST重定向為GET
           mRequest.HTTPMethod = @"GET";
           mRequest.HTTPBody = nil;
       }
       [mRequest setValue:nil forHTTPHeaderField:@"host"];
       

Cookie 場景重定向問題

之前提到的 Cookie 方案無法解決iOS11之前系統的 302 請求的 Cookie 問題,比如,第一個請求是 http://www.a.com ,我們通過在 request header 裡帶上 Cookie 解決該請求的 Cookie 問題,接著頁面302跳轉到 http://www.b.com ,這個時候 http://www.b.com 這個請求就可能因為沒有攜帶 cookie 而無法訪問。當然,由於每一次頁面跳轉前都會呼叫回撥函式:

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;

可以在該回撥函式裡攔截302請求,copy request,在 request header 中帶上 cookie 並重新 loadRequest。不過這種方法依然解決不了頁面 iframe 跨域請求的 Cookie 問題,畢竟-[WKWebView loadRequest:]只適合載入 mainFrame 請求。

如果通過之前篇幅裡提到的 iOS11 的新 API 進行處理,也就不會有該問題。

相關的文章:

補充說明:

概念 解釋 舉例
host 可以是 IP 也可以是 FQDN。 www.xxx.com 或 1.1.1.1
FQDN fully qualified domain name,由主機名和域名兩部分組成 www.xxx.com
域名 域名分為全稱和簡稱,全稱就是FQDN、簡稱就是 FQDN 不包括主機名的部分 比如:xxx.com ,也就是www.xxx.com 這個 FQDN 中,www 是主機名,xxx.com 是域名。

文中部分提到的域名,如果沒有特殊說明均指的是 FQDN。


相關文章