最近App彷佛有報異常是DNS沒法解析,嘗試解決此問題.蒐集到的資料不多,甚至連AFN原做者都斷定這多是一個無解的問題,參見: https://github.com/AFNetworking/AFNetworking/issues/2954,不過最終仍是靠着stackoverflow上的一丁點提示,順利找到並聚集成了一個可用的解決方案.大喜,與君共享!html
經過IP直接訪問網站,能夠解決DNS劫持問題.DNS劫持,能夠經過修改電腦的host文件模擬.若是是HTTP請求,使用ip地址直接訪問接口,配合header中Host字段帶上原來的域名信息便可;若是是 https請求,會很麻煩,須要 Overriding TLS Chain Validation Correctly
;curl 中有一個 -resolve
方法能夠實現使用指定ip訪問https網站,iOS中集成curl庫應該也能夠,不過改動太大,未驗證;對於服務器IP常常變的狀況,可能須要使用httpDNS服務,參見:https://www.dnspod.cn/httpdns.ios
在Info.plist中添加NSAppTransportSecurity類型Dictionary,在NSAppTransportSecurity下添加NSAllowsArbitraryLoads類型Boolean,值設爲YES.這些原本是用來解決iOS9下,容許HTTP請求訪問網絡的,固然做用不止這些.具體緣由感興趣的自行google.git
給 AFURLConnectionOperation 類添加新屬性:github
/** 可信任的域名,用於支持經過ip訪問此域名下的https連接. Trusted domain, this domain for support via IP access HTTPS links. */ @property(nonatomic, strong) NSMutableArray * trustHostnames;
給 AFURLConnectionOperation 實現的代理方法: - (void)connection:(NSURLConnection *)connection
willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge 添加添加可信任的域名的相關邏輯代碼:服務器
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { if (self.authenticationChallenge) { self.authenticationChallenge(connection, challenge); return; } if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { SecTrustRef serverTrust = challenge.protectionSpace.serverTrust; /* 添加可信任的域名,以支持:直接使用ip訪問特定https服務器. Add trusted domain name to support: direct use of IP access specific HTTPS server.*/ for (NSString * trustHostname in [self trustHostnames]) { serverTrust = AFChangeHostForTrust(serverTrust, trustHostname); } ....
參考Apple官方文檔,實現自定義的添加可信域名的函數: AFChangeHostForTrust網絡
static inline SecTrustRef AFChangeHostForTrust(SecTrustRef trust, NSString * trustHostname) { if ( ! trustHostname || [trustHostname isEqualToString:@""]) { return trust; } CFMutableArrayRef newTrustPolicies = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); SecPolicyRef sslPolicy = SecPolicyCreateSSL(true, (CFStringRef)trustHostname); CFArrayAppendValue(newTrustPolicies, sslPolicy); #ifdef MAC_BACKWARDS_COMPATIBILITY /* This technique works in OS X (v10.5 and later) */ SecTrustSetPolicies(trust, newTrustPolicies); CFRelease(oldTrustPolicies); return trust; #else /* This technique works in iOS 2 and later, or OS X v10.7 and later */ CFMutableArrayRef certificates = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); /* Copy the certificates from the original trust object */ CFIndex count = SecTrustGetCertificateCount(trust); CFIndex i=0; for (i = 0; i < count; i++) { SecCertificateRef item = SecTrustGetCertificateAtIndex(trust, i); CFArrayAppendValue(certificates, item); } /* Create a new trust object */ SecTrustRef newtrust = NULL; if (SecTrustCreateWithCertificates(certificates, newTrustPolicies, &newtrust) != errSecSuccess) { /* Probably a good spot to log something. */ return NULL; } return newtrust; #endif }
使用AOP方法,重寫 AFURLConnectionOperation 的trustHostnames屬性:app
/* 使用AOP方式,指定可信任的域名, 以支持:直接使用ip訪問特定https服務器.*/ [AFURLConnectionOperation aspect_hookSelector:@selector(trustHostnames) withOptions:AspectPositionInstead usingBlock: ^(id<AspectInfo> info){ __autoreleasing NSArray * trustHostnames = @[@"www.example.com"]; NSInvocation *invocation = info.originalInvocation; [invocation setReturnValue:&trustHostnames]; }error:NULL];
此處用到的是一個 iOS AOP庫,不熟悉的點這裏: http://www.ios122.com/2015/08/aspects/.dom