前言
這兩天公司選所謂的先進(jìn)個(gè)人,結(jié)果也是我最滿意的,沒(méi)選上但能得到自己身邊的伙伴對(duì)自己的認(rèn)可,很開(kāi)心,感冒也好了很多!雖然知道大家選我,只是對(duì)我工作層面的認(rèn)可,與人品無(wú)關(guān),但我寧愿伙伴能對(duì)我的人品層面也有同等認(rèn)可。
畢業(yè)已近兩年,出來(lái)工作時(shí)間總算起來(lái),也有快三年了。從開(kāi)始跟著師傅,到慢慢獨(dú)立,再到帶新伙伴一起做項(xiàng)目,這一路走來(lái),有過(guò)無(wú)助、有過(guò)懷疑,有過(guò)為伙伴“甘愿獨(dú)自離去”的沖動(dòng),有過(guò)低級(jí)趣味的誘惑,有過(guò)感動(dòng),有過(guò)堅(jiān)定,說(shuō)我傻B也好,說(shuō)我怎樣也罷,我都全盤接受。
說(shuō)實(shí)話,有時(shí)也會(huì)矛盾。我是一個(gè)“虛心聽(tīng)取別人意見(jiàn)”的人,別人的建議我反思后,除了性格使然部分,其他大部分我都會(huì)調(diào)整,當(dāng)然你也可以理解為沒(méi)有主見(jiàn),設(shè)計(jì)、測(cè)試怎么說(shuō)就怎么改,領(lǐng)導(dǎo)安排工作也盡量去做。慢慢的對(duì)自己的工作方式也有過(guò)疑問(wèn),這樣做是否合適?也有貴人給我提過(guò)一些東西,說(shuō)讓我學(xué)會(huì)表達(dá)自己,為此,我也做出些自己的改變,在技術(shù)方面,我開(kāi)始表達(dá)自己的想法,不過(guò)有時(shí)會(huì)不注意表達(dá)的方法和方式,也會(huì)給領(lǐng)導(dǎo)給自己造成些許困惑。
不管怎樣,猴年即將過(guò)去,這恐怕是年前最后一次項(xiàng)目上線了,給大家分享下前幾天HTTPS證書(shū)校驗(yàn)的一些東西。
說(shuō)好的寫(xiě)篇《iOS自簽名HTTPS證書(shū)單向校驗(yàn)方案》呢,又扯了這么多閑篇,對(duì)大家不住。
HTTPS簡(jiǎn)析
雖說(shuō)在去年圣誕節(jié)前夕蘋(píng)果發(fā)出公告要推遲ATS適配截止時(shí)間,但既然它在iOS9.0始推出App Transport Security,適配HTTPS是早晚的事,總要做好技術(shù)儲(chǔ)備。以鄙人淺顯的技術(shù)而言,我覺(jué)得iOS APP適配HTTPS主要涉及三方面適配:
- 普通網(wǎng)絡(luò)請(qǐng)求;
-
H5頁(yè)面加載; -
SDWebImage加載HTTPS圖片(一般公司測(cè)試庫(kù)會(huì)用到自簽名證書(shū))。
所謂HTTPS,即HTTP+SSL/TSL,底部也就是在HTTP協(xié)議層和TCP/IP協(xié)議層之間添加安全傳輸層協(xié)議SSL,從而達(dá)到對(duì)HTTP數(shù)據(jù)包加密傳輸?shù)哪康模?/p>
sequenceDiagram
participant Client
participant Server
Client->>Server: 以明文傳輸數(shù)據(jù),主要有客戶端支持的SSL版本等客戶端支持的加密信息
Server-->>Server: 服務(wù)端選擇加密方式
Server-->>Client: 服務(wù)端給客戶端返回SSL版本、隨機(jī)數(shù)等信息
Client->>Client: 校驗(yàn)服務(wù)端證書(shū)是否合法、產(chǎn)生隨機(jī)數(shù)
Client->>Server: 使用服務(wù)端隨機(jī)數(shù)加密數(shù)據(jù)發(fā)給服務(wù)端
Server-->>Server: 服務(wù)端使用私鑰解密客戶端數(shù)據(jù)
Server-->>Client: 使用收到的隨機(jī)數(shù),加密數(shù)據(jù),返回給客戶端
Client->>Client: 客戶端使用公鑰解密數(shù)據(jù)
簡(jiǎn)書(shū)不支持MarkDown高級(jí)語(yǔ)法,上面的MarkDown文本對(duì)應(yīng)下圖:

iOS適配HTTPS注意點(diǎn)
- 網(wǎng)絡(luò)請(qǐng)求NSURLConnection/NSURLSession
- H5頁(yè)面適配WKWebView/UIWebView
- SDWebImage圖片加載適配
網(wǎng)絡(luò)請(qǐng)求部分
NSURLConnection適配
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
{
NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
}
else
{
if ([challenge previousFailureCount] == 0)
{
[[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
}
else
{
[[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
}
}
}
NSURLSession適配
/** disposition:如何處理證書(shū)
NSURLSessionAuthChallengeUseCredential 使用證書(shū)
NSURLSessionAuthChallengePerformDefaultHandling 忽略證書(shū) 默認(rèn)的做法
NSURLSessionAuthChallengeCancelAuthenticationChallenge 取消請(qǐng)求,忽略證書(shū)
NSURLSessionAuthChallengeRejectProtectionSpace 拒絕,忽略證書(shū)
*/
#pragma mark - NSURLSessionDelegate代理方法 HTTPS ---開(kāi)始---
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
{
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
// 判斷服務(wù)器返回的證書(shū)是否是服務(wù)器信任的
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
{
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if (credential)
{
disposition = NSURLSessionAuthChallengeUseCredential; // 使用證書(shū)
}
else
{
disposition = NSURLSessionAuthChallengePerformDefaultHandling; // 忽略證書(shū) 默認(rèn)的做法
}
}
else
{
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge; // 取消請(qǐng)求,忽略證書(shū)
}
if (completionHandler)// 安裝證書(shū)
{
completionHandler(disposition, credential);
}
}
H5頁(yè)面適配
基于WKWebView的H5頁(yè)面HTTPS適配
#pragma mark: WKWebView的https配置
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler
{
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
{
NSURLCredential *card = [[NSURLCredential alloc]initWithTrust:challenge.protectionSpace.serverTrust];
completionHandler(NSURLSessionAuthChallengeUseCredential,card);
}
}
基于UIWebView的H5頁(yè)面HTTPS適配
詳情見(jiàn):Stretch's stackoverflow答案
#pragma mark - Webview delegate
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
{
NSLog(@"Did start loading: %@ auth:%d", [[request URL] absoluteString], _authenticated);
if (!_authenticated)
{
_authenticated = NO;
_urlConnection = [[NSURLConnection alloc] initWithRequest:_request delegate:self];
[_urlConnection start];
return NO;
}
return YES;
}
#pragma mark - NURLConnection delegate
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
{
NSLog(@"WebController Got auth challange via NSURLConnection");
if ([challenge previousFailureCount] == 0)
{
_authenticated = YES;
NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
[challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
} else
{
[[challenge sender] cancelAuthenticationChallenge:challenge];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
{
NSLog(@"WebController received response via NSURLConnection");
// remake a webview call now that authentication has passed ok.
_authenticated = YES;
[_web loadRequest:_request];
// Cancel the URL connection otherwise we double up (webview + url connection, same url = no good!)
[_urlConnection cancel];
}
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}
SDWebImage加載HTTPS圖片適配
低版本的SDWebImage是對(duì)NSURLConnection做的封裝,新版本是對(duì)NSURLSession做的封裝,適配HTTPS圖片,記得確定自己使用的SDWebImage版本的SDWebImageDownloaderOperation類是否做過(guò)校驗(yàn)。
如果SDWebImage使用的是NSURLConnection看看文件是否有:
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
否則查看SDWebImageDownloaderOperation文件中是否有
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
確定好之后,如果所有圖片均是HTTPS的,那么我們可以直接修改UIImageView+WebCache文件
- (void)sd_setImageWithURL:(nullable NSURL *)url
{
// [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil];
[self sd_setImageWithURL:url placeholderImage:nil options:SDWebImageAllowInvalidSSLCertificates progress:nil completed:nil]; // HTTPS配置問(wèn)題
}
- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder
{
// [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:nil];
[self sd_setImageWithURL:url placeholderImage:placeholder options:SDWebImageAllowInvalidSSLCertificates progress:nil completed:nil]; // HTTPS配置問(wèn)題
}
其實(shí)質(zhì)就是將option設(shè)置為SDWebImageAllowInvalidSSLCertificates,看SDWebImageDownloaderOperation文件你會(huì)發(fā)現(xiàn),SDWebImage采用直接忽略證書(shū)驗(yàn)證的方式加載的,所以設(shè)置SDWebImageAllowInvalidSSLCertificates才有效。

如有個(gè)別特殊情況,要支持某些HTTP的圖片,按照上面方法適配后,還需在plist的Exception Domains添加響應(yīng)配置,詳情見(jiàn)蘋(píng)果APP接入HTTPS延遲截止時(shí)間是妥協(xié)嗎。