在App中通過IP直連的方式訪問API

一直有用戶反映,不管通過通過手機端、還是PC端訪問我們的產品都會不定時出現域名劫持的問題。為了解決這個問題,我們只能繞過傳統的運營商域名解析,通過IP直接訪問服務。本文對App中集成HttpDNS作簡要介紹。

一、HTTPDNS介紹:

httpDNS是阿里提供的面向移動端的域名解析產品,提供了面向移動端的SDK,客戶端可以通過傳入域名的方式調用,SDK會直接返回解析出的IP地址。

NSString *ip = [[HttpDnsService sharedInstance] getIpByHostAsync:Domain];

二、需求描述:

對項目中: 1.原生圖片的請求, 2.H5網頁中請求,分別做IP直連處理。

三、實現方案:

  • 實現原理:

通過注冊NSURLProtocol,攔截所有請求,過濾出相應的圖片請求及H5網頁請求,將請求的url中的域名替換為IP后,重新發起請求,獲取到響應數據后,回調給URL Loading System。

  • 實現過程:
1.攔截請求

由于原生圖片和H5網頁中的請求需要分開處理以便于實現通過降級開關分別控制,所以注冊了兩個NSURLProtocol分別處理這兩項業務,具體策略為:
YH_ImageProtocol在攔截到請求后,按照URL后綴(是否包含:.jpg/.jpeg/.png/.gif)過濾出圖片的URL。
YH_WebProtocol在攔截到請求后,排除圖片的URL,則認為是需要攔截的請求。

2. 手動發起請求

攔截到請求后,需要根據協議分別做處理:如果是HTTP請求,使用NSURLSession重新發起請求,獲取到響應的數據后,回調給URL Loaidng System。如果是HTTPS請求,由于當前請求URL的域名被替換成了IP地址,請求URL中的host也會被替換成HTTPDNS解析出來的IP,導致服務器獲取到的域名為解析后的IP,無法找到匹配的證書,只能返回默認的證書或者不返回,所以會出現SSL/TLS握手不成功的錯誤。為了解決這個問題,我們需要hook HTTPS訪問前SSL連接過程,根據網絡請求頭部域中的HOST信息,設置SSL連接PeerHost的值,之后根據服務器返回的證書執行驗證過程。所以在攔截網絡請求后,使用CFHTTPMessageRef創建NSInputStream實例進行Socket通信,并設置其kCFStreamSSLPeerName的值:

// 創建CFHTTPMessage對象的輸入流
CFReadStreamRef readStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault,cfrequest);
inputStream = (__bridge_transfer NSInputStream *) readStream;
    
// 設置SNI host信息
NSString *host = [curRequest.allHTTPHeaderFields objectForKey:@"host"];
    if (!host) {
        host = curRequest.URL.host;
    }
    [inputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL forKey:NSStreamSocketSecurityLevelKey];
    NSDictionary *sslProperties = [[NSDictionary alloc] initWithObjectsAndKeys:
                                   host, (__bridge id) kCFStreamSSLPeerName,
                                   nil];
    [inputStream setProperty:sslProperties forKey:(__bridge_transfer NSString *) kCFStreamPropertySSLSettings];
    [inputStream setDelegate:self];
3.重定向

當返回的StatusCode在300、400之間,且header中location字段中取出合法的URL時,用該URL初始化新的請求,在protocol內部重新執行一遍之前的流程。

四、碰到的問題及解決方法:

  • GZIP
    之前在測試過程中發現,用Webview加載官網時,頁面顯示亂碼。經排查,確認是返回的content-type為gzip,因為未解壓導致頁面無法識別。為此,我們在收到響應后,先判斷content類型,如果為gzip,先進行解壓再回調給相應的client。
  • CSS文件中通過相對路徑的方式引用的靜態資源無法加載
    該問題發生的具體原因是:在WebView中的請求被攔截,域名改為IP直連后,CSS文件中通過相對路徑引用的靜態資源(包括iconfont和少量圖片)的url直接沿用了CSS文件URL中的IP地址作為域名,跳過了域名解析的步驟,且header中的HOST字段未設置為相應的域名。最終導致無法通過SNI擴展的方式獲取到SSL證書,建連失敗。我們的解決方案是保存好IP地址和域名的映射關系,碰到前述問題時,能夠獲取到IP地址對應的域名,設置給HOST,以保證SSL握手成功。

五、待改進的地方:

目前的業務需求是,攔截到的H5請求,全部強制轉為HTTPS方式請求。這種情況下會導致一些服務端不支持HTTPS的請求失敗,尤其跳轉到一些第三方網站的頁面。為避免該問題,我們應該提供一種容錯機制,當強制使用HTTPS的方式去打開頁面時,如果SSL握手失敗,可以再改為HTTP的方式去請求。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,915評論 18 139
  • https://nodejs.org/api/documentation.html 工具模塊 Assert 測試 ...
    KeKeMars閱讀 6,396評論 0 6
  • 從三月份找實習到現在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發崗...
    時芥藍閱讀 42,366評論 11 349
  • Http協議詳解 標簽(空格分隔): Linux 聲明:本片文章非原創,內容來源于博客園作者MIN飛翔的HTTP協...
    Sivin閱讀 5,252評論 3 82
  • 一、概念(載錄于:http://www.cnblogs.com/EricaMIN1987_IT/p/3837436...
    yuantao123434閱讀 8,434評論 6 152