在App中通過(guò)IP直連的方式訪問(wèn)API

一直有用戶反映,不管通過(guò)通過(guò)手機(jī)端、還是PC端訪問(wèn)我們的產(chǎn)品都會(huì)不定時(shí)出現(xiàn)域名劫持的問(wèn)題。為了解決這個(gè)問(wèn)題,我們只能繞過(guò)傳統(tǒng)的運(yùn)營(yíng)商域名解析,通過(guò)IP直接訪問(wèn)服務(wù)。本文對(duì)App中集成HttpDNS作簡(jiǎn)要介紹。

一、HTTPDNS介紹:

httpDNS是阿里提供的面向移動(dòng)端的域名解析產(chǎn)品,提供了面向移動(dòng)端的SDK,客戶端可以通過(guò)傳入域名的方式調(diào)用,SDK會(huì)直接返回解析出的IP地址。

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

二、需求描述:

對(duì)項(xiàng)目中: 1.原生圖片的請(qǐng)求, 2.H5網(wǎng)頁(yè)中請(qǐng)求,分別做IP直連處理。

三、實(shí)現(xiàn)方案:

  • 實(shí)現(xiàn)原理:

通過(guò)注冊(cè)NSURLProtocol,攔截所有請(qǐng)求,過(guò)濾出相應(yīng)的圖片請(qǐng)求及H5網(wǎng)頁(yè)請(qǐng)求,將請(qǐng)求的url中的域名替換為IP后,重新發(fā)起請(qǐng)求,獲取到響應(yīng)數(shù)據(jù)后,回調(diào)給URL Loading System。

  • 實(shí)現(xiàn)過(guò)程:
1.攔截請(qǐng)求

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

2. 手動(dòng)發(fā)起請(qǐng)求

攔截到請(qǐng)求后,需要根據(jù)協(xié)議分別做處理:如果是HTTP請(qǐng)求,使用NSURLSession重新發(fā)起請(qǐng)求,獲取到響應(yīng)的數(shù)據(jù)后,回調(diào)給URL Loaidng System。如果是HTTPS請(qǐng)求,由于當(dāng)前請(qǐng)求URL的域名被替換成了IP地址,請(qǐng)求URL中的host也會(huì)被替換成HTTPDNS解析出來(lái)的IP,導(dǎo)致服務(wù)器獲取到的域名為解析后的IP,無(wú)法找到匹配的證書,只能返回默認(rèn)的證書或者不返回,所以會(huì)出現(xiàn)SSL/TLS握手不成功的錯(cuò)誤。為了解決這個(gè)問(wèn)題,我們需要hook HTTPS訪問(wèn)前SSL連接過(guò)程,根據(jù)網(wǎng)絡(luò)請(qǐng)求頭部域中的HOST信息,設(shè)置SSL連接PeerHost的值,之后根據(jù)服務(wù)器返回的證書執(zhí)行驗(yàn)證過(guò)程。所以在攔截網(wǎng)絡(luò)請(qǐng)求后,使用CFHTTPMessageRef創(chuàng)建NSInputStream實(shí)例進(jìn)行Socket通信,并設(shè)置其kCFStreamSSLPeerName的值:

// 創(chuàng)建CFHTTPMessage對(duì)象的輸入流
CFReadStreamRef readStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault,cfrequest);
inputStream = (__bridge_transfer NSInputStream *) readStream;
    
// 設(shè)置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.重定向

當(dāng)返回的StatusCode在300、400之間,且header中l(wèi)ocation字段中取出合法的URL時(shí),用該URL初始化新的請(qǐng)求,在protocol內(nèi)部重新執(zhí)行一遍之前的流程。

四、碰到的問(wèn)題及解決方法:

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

五、待改進(jìn)的地方:

目前的業(yè)務(wù)需求是,攔截到的H5請(qǐng)求,全部強(qiáng)制轉(zhuǎn)為HTTPS方式請(qǐng)求。這種情況下會(huì)導(dǎo)致一些服務(wù)端不支持HTTPS的請(qǐng)求失敗,尤其跳轉(zhuǎn)到一些第三方網(wǎng)站的頁(yè)面。為避免該問(wèn)題,我們應(yīng)該提供一種容錯(cuò)機(jī)制,當(dāng)強(qiáng)制使用HTTPS的方式去打開頁(yè)面時(shí),如果SSL握手失敗,可以再改為HTTP的方式去請(qǐng)求。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評(píng)論 19 139
  • https://nodejs.org/api/documentation.html 工具模塊 Assert 測(cè)試 ...
    KeKeMars閱讀 6,597評(píng)論 0 6
  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂(lè)視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,755評(píng)論 11 349
  • Http協(xié)議詳解 標(biāo)簽(空格分隔): Linux 聲明:本片文章非原創(chuàng),內(nèi)容來(lái)源于博客園作者M(jìn)IN飛翔的HTTP協(xié)...
    Sivin閱讀 5,334評(píng)論 3 82
  • 一、概念(載錄于:http://www.cnblogs.com/EricaMIN1987_IT/p/3837436...
    yuantao123434閱讀 8,726評(píng)論 6 152

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