(IOS)如何處理不受信任的http網(wǎng)路憑證 (WebView,下載檔案)

本篇使用Swift 并附上官方文檔

前陣子接了(公司A)一個專案,再加上要畢業(yè)了,學(xué)校各種忙碌,距離上一篇文章也有好大一段時間...,也是因?yàn)檫@個專案碰到了些小問題,才想來寫寫筆記。

一、起因

公司A有個只限內(nèi)網(wǎng)用的公文系統(tǒng)(似乎是用java寫的網(wǎng)頁?),到目前為止都是單純在Windows上的小程式利用『URL Scheme』跟這個系統(tǒng)互相丟接資料,我則是要負(fù)責(zé)寫一個IOS App 跟此系統(tǒng)做一樣的事。但我是承包的,無法在公司A的內(nèi)網(wǎng)下測試,經(jīng)過一番討論,他們決定將系統(tǒng)做個測試用的灌在windows虛擬機(jī)上,我在Mac上執(zhí)行就等于我跟這個系統(tǒng)相同內(nèi)網(wǎng)。

二、前置測試 ???

假設(shè)各個內(nèi)網(wǎng)ip如下:

(A)Mac:192.168.1.1

(B)Mac上的Windows虛擬機(jī):192.168.1.2

(C)實(shí)機(jī)Iphone:192.168.1.3

(D)系統(tǒng)網(wǎng)址:https://192.168.1.2:8888/Domain

A ping B,C -> OK ??、 B ping A,C -> OK ??

A 預(yù)覽 D -> OK ??、 B 預(yù)覽 D -> OK ??

接下來讓我們看看我要說的 C 預(yù)覽 D 的狀況

三、問題一 (UIWebView 預(yù)覽 D) ??

  • 第一種嘗試是利用WebView, 為什么首選它呢?

    因?yàn)闃I(yè)主不希望在兩個App之間做過多的切換(一整個流程下來可能跳轉(zhuǎn)兩~三次),也不希望使用者在下次開啟Safari時,還停留在系統(tǒng)的頁面。

  • NSURLErrorDomain: -1003?

    WebView就在我LoadRequest時跳出這么一個錯誤,看到ErrorDomain時,我也沒有多想,就直接認(rèn)為應(yīng)該是我DNS沒設(shè)定好之類的問題,于是又重開了虛擬機(jī),重啟站臺...!@#!$????

  • Safari可開啟?

    會突然使用Safari是因?yàn)槲也幌胍恢敝匦翨uild,內(nèi)心想說結(jié)果一樣,沒想到,跳出來一個這樣的提示:

    Safari開啟系統(tǒng)

    按下"Continue"后,就顯示出系統(tǒng)的頁面了!

  • 不信任的的憑證

    有了這個彈窗的提醒,我才明白問題的癥結(jié)點(diǎn)原來是“憑證”,原來是之前IOS9的新設(shè)定:App Transport Security (ATS)

    ATS參考網(wǎng)址:https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW33

    基本上ATS就是為了要確保你的App更安全,會擋兩樣?xùn)|西一個就是沒有Security的Http一個是不信任憑證的Https,不會無意間跑到釣魚網(wǎng)站之類的。

三、問題一解法??

  • 設(shè)定Info.plist

    第一個方法最簡單就是直接關(guān)掉ATS,直接在Info.plist增加以下其中一對Key Value Pair

    Info.plist
    1.Allow Arbitary Loads (NSAllowArbitrayLoads)
    用途是?解除所有連線的ATS限制,這里指的所有連線包括URLRequest,URLConnection,URLSession,UIWebView....等。

    但是官方文件里也很表明寫了,只要Allow Arbitary Loads這個值被設(shè)為True,就沒有辦法通過上架審核,所以我不采用此方法。


    2.Exception Domain (NSExceptionDomains)

    用途是?排除某個Domain使其不受ATS的限制。
    個人認(rèn)為這個Key比較適合拿來使用,如果你明確知道自己會在某個Domain之下的話。
    這個Key我不知道會不會影響上架?

    官方文檔:https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW35


  • HTTPS Server Trust Evaluation

    https://developer.apple.com/library/content/technotes/tn2232/_index.html#//apple_ref/doc/uid/DTS40012884-CH1-SECWEBVIEW

    另一種方法就是實(shí)作官方文檔里的『Trust Customization for Specific APIs』,也就是自定義某個要求的憑證檢查

    1.URLSession


    依照官方文檔,我們需要實(shí)作 urlSession(_:task:didReceive:completionHandler:),如果憑證無法驗(yàn)證,會發(fā)出一個Challenge供你判斷你要拒絕這個連線還是提供一個憑證來解決:

    • 遵守URLSessionDelegate

      let session = Foundation.URLSession(configuration: sessionConfig, delegate: self, delegateQueue: nil)
      
    • 處理 - 『Challenge』

      challenge : URLAuthenticationChallenge

      Challenge這個詞并不是ios或CocoaFrameWork才有的,而是互聯(lián)網(wǎng)中伺服器向使用者端發(fā)出的『Challenge–response authentication』,是一個用來驗(yàn)證用戶或網(wǎng)絡(luò)提供者的協(xié)議,會要求使用者回傳一些資訊,帳號、密碼、憑證...等,在下面會說說有哪些。

      https://developer.apple.com/reference/foundation/urlauthenticationchallenge

      https://zh.wikipedia.org/zh-cn/CHAP

      func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Swift.Void)
      

    {
    //1
    let protectionspace = challenge.protectionSpace
    //2
    let authMethod = protectionspace.authenticationMethod
    if authMethod == NSURLAuthenticationMethodServerTrust
    {
    //3
    let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
    //4
    let input:URLSession.AuthChallengeDisposition = .useCredential
    //5
    completionHandler(input, credential)
    }
    }
    ```

     1.***Challenge.protectionSpace*** : URLProtectionSpace -> 包含了這個 authentication request的host,port...等,讓你判斷該用什么**credential(證書)**應(yīng)付。
     
     2.***authenticationMethod*** : 這是我剛剛提到的,伺服器要求認(rèn)證的方法,這個方法就會決定接下來要做的事,像是:NSURLAuthenticationMethodHTTPBasic:要求使用者回傳帳號跟密碼,而我們要處理的是NSURLAuthenticationMethodServerTrust,要求使用者回傳一個憑證。
     
     3.***URLCredential*** : 這個類有幾種差異滿大的Init(),主要就是看authenticationMethod來決定你要回傳的是什么。![image.png](http://upload-images.jianshu.io/upload_images/3776017-a0fe3c2e078cbed5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)我們這次要做的是憑證的處理,所以用第二個建構(gòu)。
             
     >https://developer.apple.com/reference/foundation/urlprotectionspace/nsurlprotectionspace_authentication_methods
     
     4.URLSession.***AuthChallengeDisposition*** : 表示行為,包括要使用憑證、取消此次要求、執(zhí)行預(yù)設(shè)動作...等列舉,我們要用憑證所以用.useCredential。
     
     5.利用completionHandle告知結(jié)果:(使用憑證,這個憑證)
    

    2.UIWebView - 回歸正題我們要用UIWebView,但官方文檔即提到無法自定義Https server trust,但我們還是要用上面的方法解決。

    棘手的地方是UIWebView只能單純LoadingRequest跟管理URLSession,必須繞道而行。

  • 實(shí)作UIWebViewDelegate記住失敗的Request,并且建立一個URLConnection。

       func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool
  {
          LastRequest = request
          return true
  }
      
       func webView(_ webView: UIWebView, didFailLoadWithError error: Error)
  {
          FailRequest = LastRequest
          if(FailRequest != nil)
          {
              let _:NSURLConnection = NSURLConnection(request: FailRequest! , delegate: self)!
          }
  }
      ```
      
*   雖然剛剛講的是URLSession,但兩個用法其實(shí)是相同的,所以我們遵守NSURLConnectionDelegate,并在最后重新Loading一次Request,這樣同個Request就可以通了。
      
  ```swift
      func connection(_ connection: NSURLConnection, willSendRequestFor challenge: URLAuthenticationChallenge)
      {
          if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust  {
          print("send credential Server Trust")
          let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
          challenge.sender!.use(credential, for: challenge)
      }
      else{
          challenge.sender!.performDefaultHandling!(for: challenge)
      }
          connection.cancel()
          SystemWebView.loadRequest(FailRequest!)
  }
  ```
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容