Alamofire,swift語言中,使用最廣的網(wǎng)絡(luò)請求框架,是
AFNetworking的swift版本,AF其實就是Alamofire的縮寫,swift下叫回這個名字,也算是回歸本初了。
關(guān)于HTTPS的證書

圖片來自網(wǎng)絡(luò)
一般我們拿到后臺人員導(dǎo)出的證書,會用到兩個,一個是后綴名為
cer的包含公鑰的CA證書,用來讓客戶端對服務(wù)器進行驗證;一個是后綴名為p12的客戶端證書,用來讓服務(wù)器對客戶端進行驗證。我們的電腦瀏覽器想要訪問服務(wù)器的話,直接安裝客戶端的p12證書就可以了,手動選擇信任服務(wù)器,瀏覽器會報不安全的警告。這里我們也能想到,我們的app在驗證的過程中,類似的也可以選擇雙向驗證或者忽略對服務(wù)器證書驗證的單項驗證。
Alamofire驗證證書
Alamofire將對https證書的驗證放在SessionManager.delegate.sessionDidReceiveChallenge這個block里進行,所以我們給這個block賦值做驗證:
//
// HTTPSManager.swift
// Test
//
// Created by Fxxx on 2018/9/30.
// Copyright ? 2018 Aaron Feng. All rights reserved.
//
import UIKit
import Alamofire
class HTTPSManager: NSObject {
func setAlamofireHttps() {
SessionManager.default.delegate.sessionDidReceiveChallenge = { (session: URLSession, challenge: URLAuthenticationChallenge) in
let method = challenge.protectionSpace.authenticationMethod
if method == NSURLAuthenticationMethodServerTrust {
//驗證服務(wù)器,直接信任或者驗證證書二選一,推薦驗證證書,更安全
return HTTPSManager.trustServerWithCer(challenge: challenge)
// return HTTPSManager.trustServer(challenge: challenge)
} else if method == NSURLAuthenticationMethodClientCertificate {
//認證客戶端證書
return HTTPSManager.sendClientCer()
} else {
//其他情況,不通過驗證
return (.cancelAuthenticationChallenge, nil)
}
}
}
//不做任何驗證,直接信任服務(wù)器
static private func trustServer(challenge: URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?) {
let disposition = URLSession.AuthChallengeDisposition.useCredential
let credential = URLCredential.init(trust: challenge.protectionSpace.serverTrust!)
return (disposition, credential)
}
//驗證服務(wù)器證書
static private func trustServerWithCer(challenge: URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?) {
var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
var credential: URLCredential?
//獲取服務(wù)器發(fā)送過來的證書
let serverTrust:SecTrust = challenge.protectionSpace.serverTrust!
let certificate = SecTrustGetCertificateAtIndex(serverTrust, 0)!
let remoteCertificateData = CFBridgingRetain(SecCertificateCopyData(certificate))!
//加載本地CA證書
let cerPath = Bundle.main.path(forResource: "你本地的cer證書文件名", ofType: "cer")!
let cerUrl = URL(fileURLWithPath:cerPath)
let localCertificateData = try! Data(contentsOf: cerUrl)
if (remoteCertificateData.isEqual(localCertificateData) == true) {
//服務(wù)器證書驗證通過
disposition = URLSession.AuthChallengeDisposition.useCredential
credential = URLCredential(trust: serverTrust)
} else {
//服務(wù)器證書驗證失敗
disposition = URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge
}
return (disposition, credential)
}
//發(fā)送客戶端證書交由服務(wù)器驗證
static private func sendClientCer() -> (URLSession.AuthChallengeDisposition, URLCredential?) {
let disposition = URLSession.AuthChallengeDisposition.useCredential
var credential: URLCredential?
//獲取項目中P12證書文件的路徑
let path: String = Bundle.main.path(forResource: "你本地的p12證書文件名", ofType: "p12")!
let PKCS12Data = NSData(contentsOfFile:path)!
let key : NSString = kSecImportExportPassphrase as NSString
let options : NSDictionary = [key : "p12證書的密碼"] //客戶端證書密碼
var items: CFArray?
let error = SecPKCS12Import(PKCS12Data, options, &items)
if error == errSecSuccess {
let itemArr = items! as Array
let item = itemArr.first!
let identityPointer = item["identity"];
let secIdentityRef = identityPointer as! SecIdentity
let chainPointer = item["chain"]
let chainRef = chainPointer as? [Any]
credential = URLCredential.init(identity: secIdentityRef, certificates: chainRef, persistence: URLCredential.Persistence.forSession)
}
return (disposition, credential)
}
}
在程序啟動進行網(wǎng)絡(luò)請求之前,調(diào)用一次setAlamofireHttps方法就好了。

image.png
PS:這里解釋一下為什么不把
setAlamofireHttps方法設(shè)置為靜態(tài),因為這個方法實際上是在執(zhí)行一個block的賦值操作,只會被調(diào)用一次,static的生命周期是從整個程序運行開始到結(jié)束,所以除了會被頻繁使用的屬性或者方法,一般要謹慎使用static避免不必要的內(nèi)存浪費,其他幾個方法是每次網(wǎng)絡(luò)請求都會調(diào)用,所以設(shè)置成靜態(tài)比較合理。
整個類里既有靜態(tài)的類方法也有實例方法,看著別扭,那有人會問,可不可以把所有方法都設(shè)置成實例方法,可以是可以,但是由于setAlamofireHttps賦值的block中調(diào)用了后續(xù)的幾個方法,會持有self,這個block又是屬于SessionManager.default這個單例的引用鏈,所以你創(chuàng)建的這個HTTPSManager這個實例對象會被一直持有無法釋放,造成浪費。
所以綜合看來,這種混合方式性能最優(yōu)。
歡迎更正錯誤和交流,回復(fù)評論和私信皆可 ??