NSURLRequest對象經(jīng)常遇到驗證挑戰(zhàn),或者從其他連接的服務(wù)器請求證書。當(dāng)請求遇到驗證挑戰(zhàn)的時候,NSURLSession類會通知它的委托,以便它們能夠相應(yīng)的執(zhí)行。
重要:URL加載系統(tǒng)的類一般不調(diào)用它們的委托來處理請求挑戰(zhàn),除非服務(wù)器的響應(yīng)包含WWW-Authenticate頭部。其他的驗證類型,例如代理驗證和TLS信任驗證,不需要此頭部。
決定如何響應(yīng)一個驗證挑戰(zhàn)
如果會話任務(wù)請求驗證,并且沒有有效的證書可用,作為請求URL的一部分或者共享的NSURLCredentialStorage,它會創(chuàng)建一個驗證挑戰(zhàn)。它首先發(fā)送URLSession:task:didReceiveChallenge:completionHandler: 到它的任務(wù)委托來處理該驗證挑戰(zhàn)。如果任務(wù)委托沒有響應(yīng)這個消息,該任務(wù)會發(fā)送URLSession:task:didReceiveChallenge:completionHandler:給它的會話委托來處理這個認(rèn)證挑戰(zhàn)。
為了繼續(xù)連接,委托由三個選項:
- 提供驗證證書。
- 嘗試無證書連接。
- 取消驗證挑戰(zhàn)。
為了幫助確定正確的操作過程,傳遞給該方法的NSURLAuthenticationChallenge實(shí)例包含關(guān)于觸發(fā)該驗證挑戰(zhàn)的信息、對挑戰(zhàn)進(jìn)行的嘗試次數(shù)、任何之前嘗試的證書、請求證書的NSURLProtectionSpace、以及挑戰(zhàn)的發(fā)送者。
如果驗證挑戰(zhàn)之前已經(jīng)有驗證但驗證失?。ɡ?,如果用戶改變了密碼或服務(wù)器),你可以通過調(diào)用驗證挑戰(zhàn)中的hproposedCredential來獲取嘗試的證書。然后該委托把這些證書通過彈出對話框顯示給用戶。
調(diào)用驗證挑戰(zhàn)的previousFailureCount,返回之前驗證嘗試的總次數(shù),包括了不同驗證協(xié)議的嘗試。該委托可以抱這些信息提供給用戶,已確定之前提供的證書是否失敗,或者限制驗證嘗試次數(shù)。
響應(yīng)驗證挑戰(zhàn)
以下三種方法是對URLSession:didReceiveChallenge:completionHandler: 或 URLSession:task:didReceiveChallenge:completionHandler:委托方法的響應(yīng)。
提供證書
要想嘗試驗證,應(yīng)用程序應(yīng)該使用驗證信息創(chuàng)建NSURLCredential對象,該驗證信息應(yīng)該符合服務(wù)器的要求。你可以通過在提供驗證挑戰(zhàn)的保護(hù)空間中調(diào)用authenticationMethod確定服務(wù)器的驗證方法。NSURLCredential支持一些驗證方法:
- HTTP基礎(chǔ)驗證(NSURLAuthenticationMethodHTTPBasic)需要用戶名和密碼。提示用戶提供必要的信息,并且使用credentialWithUser:password:persistence:創(chuàng)建NSURLCredential對象。
- HTTP摘要驗證(NSURLAuthenticationMethodHTTPDigest),類似基礎(chǔ)驗證,需要用戶名和密碼。(該摘要是自動生成的。)提示用戶提供必要的信息,并使用credentialWithUser:password:persistence:創(chuàng)建NSURLCredential對象。
- 客戶端證書驗證(NSURLAuthenticationMethodClientCertificate)要求系統(tǒng)身份和所有必要的證書通過服務(wù)器進(jìn)行身份驗證。使用credentialWithIdentity:certificates:persistence:創(chuàng)建NSURLCredential對象。
- 服務(wù)器信任驗證(NSURLAuthenticationMethodServerTrust)需要由驗證挑戰(zhàn)的保護(hù)空間提供信任(trust)。使用credentialForTrust:創(chuàng)建NSURLCredential對象。
在你創(chuàng)建了NSURLCredential對象之后,使用提供的完成處理程序代碼塊把這個對象傳遞給驗證挑戰(zhàn)的發(fā)送者。
無證書連接
如果委托選擇不向驗證挑戰(zhàn)提供證書,它可以嘗試無驗證的繼續(xù)連接。把下面值之一傳遞給完成處理程序代碼塊:
- NSURLSessionAuthChallengePerformDefaultHandling處理請求,就像委托不提供委托方法來處理挑戰(zhàn)一樣。
- NSURLSessionAuthChallengeRejectProtectionSpace拒絕挑戰(zhàn)。根據(jù)服務(wù)器響應(yīng)允許的驗證類型,URL加載類或許調(diào)用這個委托方法不止一次,以獲取額外的保護(hù)空間。
取消連接
通過給提供的完成處理程序代碼塊傳遞NSURLSessionAuthChallengeCancelAuthenticationChallenge,委托也可以選擇取消驗證挑戰(zhàn)。
驗證樣例
代碼清單4-1 展示了,通過創(chuàng)建NSURLCredential實(shí)例響應(yīng)挑戰(zhàn)。該實(shí)例使用通過應(yīng)用程序的首選項提供的用戶名和密碼創(chuàng)建。如果該驗證之前失敗,它會取消認(rèn)證挑戰(zhàn)并通知用戶。
代碼清單 4-1 使用URLSession:didReceiveChallenge:completionHandler:委托方法的示例
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler
{
if ([challenge previousFailureCount] == 0) {
NSURLCredential *newCredential = [NSURLCredential credentialWithUser:[self preferencesName]
password:[self preferencesPassword]
persistence:NSURLCredentialPersistenceNone];
completionHandler(NSURLSessionAuthChallengeUseCredential, newCredential);
} else {
// Inform the user that the user name and password are incorrect
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
}
}
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
guard challenge.previousFailureCount == 0 else {
challenge.sender?.cancel(challenge)
// Inform the user that the user name and password are incorrect
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
let proposedCredential = URLCredential(user: self.preferencesName, password: self.preferencesPassword, persistence: .none)
completionHandler(.useCredential, proposedCredential)
}
如果驗證挑戰(zhàn)沒有被會話或任務(wù)委托處理,且證書不可用或使用它們驗證失敗,continueWithoutCredentialForAuthenticationChallenge消息會通過底層的實(shí)現(xiàn)發(fā)送。
執(zhí)行自定義的TLS鏈驗證
在NSURL系列API中,TLS鏈驗證通過app驗證委托方法處理,而不是提供驗證用戶(或app)的證書給服務(wù)器。App會在TLS握手期間檢查服務(wù)器提供的證書,然后告訴URL加載系統(tǒng)是否應(yīng)該接受或拒絕這些證書。
如果你需要以非標(biāo)準(zhǔn)的方式執(zhí)行鏈驗證(例如,為測試接收一個特定的自簽名證書),app必須實(shí)現(xiàn)URLSession:didReceiveChallenge:completionHandler: 或 URLSession:task:didReceiveChallenge:completionHandler:委托方法。如果兩者都實(shí)現(xiàn),則會話級別的方法負(fù)責(zé)處理驗證。
在你的驗證處理委托方法中,你應(yīng)該檢查挑戰(zhàn)保護(hù)空間是否有NSURLAuthenticationMethodServerTrust的驗證類型,如果有,從保護(hù)空間獲取serverTrust信息。