httpdns實戰感受(ios開發篇章)
近期項目中為了解決域名問題在項目中集成了阿裏雲的第三方域名解析服務HttpDns,現在描述一下我的實戰感受
首先提出幾個問題,本文就圍繞著這幾個問題來進行編輯。
1. 為什麽要集成HttpDns?
2. 集成HttpDns的步驟?
3. 集成過程中容易遇到哪些問題?
第一個問題:
客戶端進行網絡請求,首先會經過運營商(移動,聯通,電信)的Local DNS。因為我們進行網絡的路徑通常都是使用域名的拼接的,所以需要經過運營商根據域名找到對應的ip,然後再訪問到公司服務器。而經過運營商的Local Dns的時候,有可能存在劫持的情況,導致訪問的結果並不是我們想要的。或者在我們訪問的網頁上打入一些廣告之類的。為了解決類似情況,我們在進行網絡請求之前需要對域名進行解析,解析的結果是對應的ip地址。那麽發送網絡請求的時候,運營商發現你的路徑是ip,就會直接"放行"。這樣可以防止域名劫持,間接的也提高了訪問的速度,因為我們中間跳過了運營商這一步。那麽怎樣對域名進行解析呢?這就涉及到第三方服務了。阿裏雲,騰訊都有域名解析的第三方服務,畢竟他們的技術比較成熟。我們公司集成的是阿裏雲的HttpDns。
第二個問題:
集成HttpDns步驟(其實文檔上也有)
第一步:需要到登陸阿裏雲賬號(沒有的註冊),然後找到HttpDns,並且開通。開通之後,就能獲取到一個AccountId,這個AccountId在項目中需要使用。然後再添加你項目中需要解析的域名,控制臺就可以看到每天域名解析的次數(當天解析的要到第二天才能看到)。
第二步:添加包含HttpDns解析功能的frameWork,並添加相應的依賴庫。然後照著文檔在appDelegate中添加相應代碼(設置AccountId,設置降級代理,預解析域名...),這個文檔上都寫的比較清楚。接下來就涉及到域名解析了,解析分兩種情況:http、https請求。 但是不管是http還是https請求都有一些共同步驟,首先將域名成ip,由於通常情況下都使用異步解析,所以有可能不能立即拿到解析後的ip(httpdns有緩存機制,首先查看緩存,緩存沒有返回nil,然後異步解析域名,解析成功之後更新緩存),所以我在程序啟動的時候就手動調用了異步解析的接口,解析項目需要使用到的域名。那麽在做網絡請求之前再解析的時候就能獲取到解析之後的iP了。然後將請求路徑中的域名替換成ip。將域名替換成ip之後直接進行網絡請求,會導致網絡請求不通過,原因是因為在我們將網絡請求中的域名替換成ip後,網絡請求中的head中host字段也會更換成ip,因為一臺服務器我們會有很多接口服務同時存在,服務器接收到請求後無法根據域名去判斷我們訪問的是哪個服務。所以就會導致網絡請求不通過。所以我們在將域名替換成ip之後需要將請求頭(header)中的host字段設置回域名。做完這些操作之後,http請求就能正常解析,能夠正常訪問了。但是https請求光做這些還不夠,由於https要做證書校驗,所以針對https請求還需要綁定證書策略,將ip重新替換成域名在執行證書校驗。以NSURLSession進行的網絡請求為例:代碼如下
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
forDomain:(NSString *)domain {
/*
* 創建證書校驗策略
*/
NSMutableArray *policies = [NSMutableArray array];
if (domain) {
[policies addObject:(__bridge_transfer id) SecPolicyCreateSSL(true, (__bridge CFStringRef) domain)];
} else {
[policies addObject:(__bridge_transfer id) SecPolicyCreateBasicX509()];
}
/*
* 綁定校驗策略到服務端的證書上
*/
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef) policies);
/*
* 評估當前serverTrust是否可信任,
* 官方建議在result = kSecTrustResultUnspecified 或 kSecTrustResultProceed
* 的情況下serverTrust可以被驗證通過,https://developer.apple.com/library/ios/technotes/tn2232/_index.html
* 關於SecTrustResultType的詳細信息請參考SecTrust.h
*/
SecTrustResultType result;
SecTrustEvaluate(serverTrust, &result);
return (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
}
#pragma mark - NSURLSessionTaskDelegate
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *_Nullable))completionHandler {
if (!challenge) {
return;
}
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
NSURLCredential *credential = nil;
/*
* 獲取原始域名信息。
*/
NSString *host = [[self.request allHTTPHeaderFields] objectForKey:@"host"];
if (!host) {
host = self.request.URL.host;
}
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if ([self evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:host]) {
disposition = NSURLSessionAuthChallengeUseCredential;
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
// 對於其他的challenges直接使用默認的驗證方案
completionHandler(disposition, credential);
}
https請求加上這些代碼之後,也就能通過證書校驗了,就能正常訪問了。
第三個問題:
容易踩的兩個坑就是 1. 將域名替換成ip之後,沒有將header中的host字段設置成域名 2. https請求沒有重新將ip替換成域名之後在做證書校驗
在我們的項目中,我遇到的兩個問題
1. 我們項目中使用的是公共的網絡請求工具類,然後使用的域名確不止一個,所以就導致當從一個域名的網絡請求切換到另一個域名的網絡請求的時候,網絡請求不通過,這是因為header中的 host字段使用的還是上一個域名,導致header中的host字段與真正請求的ip不匹配
解決辦法:在每一個網絡請求之前都根據當前網絡請求的域名解析ip,將ip替換成域名,並且每一個請求都將header中的host字段設置成當前域名。不做統一設置(如果項目中使用的域名只有 一個則可以統一設置)
2. 我們公司解析出來的ip在某些地區受運營商的限制,網絡請求無法通過。在相同的網絡狀態下,不管是使用運營商的Local DNS還是使用第三方的HttpDns,解析的都是同一個域名,所以即 使使用了HttpDns解析,我們的項目在受限制的地區任然無法訪問公司服務
解決辦法:後臺增加域名,當網絡請求失敗之後,進行域名切換,這樣就能解決在受限地區無法訪問的問題
總而言之,集成HttpDns有很多好處。1. 防止域名劫持 2. 優化網絡,提高網絡請求的訪問速度
httpdns實戰感受(ios開發篇章)