前言
最近在公司寫了個小程序來為iOS應用中的圖片瘦身,進而減小APP大小,減少用戶下載時的流量。
瘦身是在一個專門為圖片瘦身的網站進行的。
地址:https://tinypng.com
這個網站提供的接口是基於https協議的,之前沒有怎麼用過https協議,現在一並總結一下。
關於HTTPS
https協議基礎請參考參考:
HTTPS的七個誤解
其實HTTPS就是安全版本的http協議,
他采用了RSA非對稱加密公私鑰對,使用SSL證書驗證保證了用戶數據在傳輸時的安全行。
下面簡單看一下http和https請求過程的異同
我們按個看下流程
1.客戶端向服務端發送基於https的請求。
2.服務端創建公私鑰對。
3.服務端把共鑰綁定在證書上面返回給客戶端。
4.客戶端驗證證書是否可靠(驗證方式有兩種,分別針對CA機構辦法的證書和自己創建的證書:1.是向頒發證書的CA機構發送請求來驗證。2.是在客戶端保存一個證書副本,來對比兩個證書,同時還會驗證是否被中間人進行了攻擊,驗證方式就是用證書的pubkey去解證書的上密文,如果和證書上的明文一直就可以確定沒有被攻擊)。
5.客戶端生成一個隨機數並用公鑰加密傳遞給服務器。
6.服務器用私鑰解密得到隨機數,根據隨機數產生對稱加密秘鑰並用私鑰對秘鑰進行加密。
7.傳遞對稱秘鑰給客戶端。
8.客戶端用公鑰解密得到對稱加密秘鑰。
以後的通信就會使用對稱加密的秘鑰來進行了,所以https其實也就第一次請求會比較慢,因為要生成通信對稱秘鑰,以後再進行通信就和http不會差很多了。
iOS對於HTTPS的支持
在說這點之前,先說說tinypng這個網站的接口。
注冊新用戶後會返回給你一串key,我們要針對這串key做https請求
該站采用的是HTTP Basic Auth認證方式(關於Basic Auth認證方式詳情參看維基百科)。
所以我們做請求的時候就需要使用添加headerfield。
返回的時候會把我們上傳圖片處理後的下載路徑傳回來,比較奇葩的是,路徑並不在響應體而是在響應頭中的Location字段內...(難道圖片就不需要保密了麼...)。
下面說說iOS該怎麼做,
iOS的NSURLConnection和NSURLSession的API都提供了很方便的API來支持https請求。
我在實際操作的時候使用的是NSURLConnection。
首先創建請求:
NSURL *url = [NSURL URLWithString:REQUEST_URL]; NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; NSString *basicAuthUsername = BASIC_AUTH_USERNAME; NSString *basicAuthPassword = BASIC_AUTH_PASSWORD; NSData *authorizationData = [[NSString stringWithFormat:@"%@:%@",basicAuthUsername,basicAuthPassword] dataUsingEncoding:NSASCIIStringEncoding]; NSString *authorizationStr = [NSString stringWithFormat:@"Basic %@",[authorizationData base64EncodedStringWithOptions:0]]; NSLog(@"%@",authorizationStr); [request setHTTPMethod:@"POST"]; [request addValue:authorizationStr forHTTPHeaderField:@"Authorization"]; [request addValue:@"*/*" forHTTPHeaderField:@"Accept"];
URL在API中提供的有,只是協議我們寫為HTTPS,然後進行Authorization頭字段的拼接,實際上就是Basic base64(用戶名:密碼)。
Accept這裡設置為了*/*,其實如果知道服務器返回類型可以直接指定application/json或者text/json之類的就行。
下面看看連接:
-(BOOL)connection:(NSURLConnection*)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace*)protectionSpace { return[protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]; } -(void)connection:(NSURLConnection*)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge*)challenge { [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge]; }
我們需要實現NSURLConnectionDelegate,然後實現上面的兩個方法。
第一個方法是判斷需要響應哪一類的安全問題,
NSString*NSURLAuthenticationMethodDefault;
NSString*NSURLAuthenticationMethodHTTPBasic;
NSString*NSURLAuthenticationMethodHTTPDigest;
NSString*NSURLAuthenticationMethodHTMLForm;
NSString*NSURLAuthenticationMethodNegotiate;
NSString*NSURLAuthenticationMethodNTLM;
NSString*NSURLAuthenticationMethodClientCertificate;
NSString*NSURLAuthenticationMethodServerTrust;
可以響應的安全問題有很多,這裡我們只響應HTTPS相關的就行,因此選擇NSURLAuthenticationMethodServerTrust。
第二個方法是處理驗證結果的,這裡我這樣寫會直接忽略證書驗證,這裡我們可以處理證書的驗證策略邏輯。
我們start connection後就會發現可以成功的調用接口了。
關於一些其他細節
寫這個小玩意還是用到了一些沒有接觸過的東西的。
下面總結一下。
1.文件實例類NSFileHandle,這個類可以拿到文件實例,比如我們想去控制文件讀寫細節就需要用到這個類,這裡使用是為了保存沒有成功請求的圖片名稱。
2.connection的異步請求做的非常好了,使用多線程請求,具體的請求線程個數由系統來判斷。
3.多線程讀寫文件使用dispatch_barrier_async方法避免資源競爭。
不足
1.寫的時候是所有上傳請求全部結束後才開始下載的,這樣效率很低,可以修改為成功上傳後就直接下載不用等待其他的文件上傳,不過這樣多線程處理會稍微麻煩一些。