一個(gè)NSURLRequest對(duì)象經(jīng)常會(huì)遇到認(rèn)證請(qǐng)求,或者需要從其所連接的服務(wù)端請(qǐng)求證書(shū)。當(dāng)需要認(rèn)證請(qǐng)求時(shí),NSURLConnection、NSURLSession和NSURLDownload類(lèi)會(huì)通知它們的代理對(duì)象,以便能正確地做處理。不過(guò)需要注意的是,URL加載系統(tǒng)只有在服務(wù)端響應(yīng)包含WWW-Authenticate頭時(shí)才會(huì)調(diào)用代理來(lái)處理認(rèn)證請(qǐng)求,而類(lèi)似于代理認(rèn)證和TLS信任驗(yàn)證這樣的認(rèn)證類(lèi)型則不需要這個(gè)頭。
確定如何響應(yīng)一個(gè)認(rèn)證請(qǐng)求
如果一個(gè)NSURLRequest對(duì)象需要認(rèn)證時(shí),則認(rèn)證請(qǐng)求方式取決于使用的對(duì)象的類(lèi)型:
如果請(qǐng)求是與NSURLSession對(duì)象關(guān)聯(lián),則所有認(rèn)證請(qǐng)求都會(huì)傳遞給代理,而不考慮認(rèn)證的類(lèi)型。
如果請(qǐng)求是與NSURLConnection或NSURLDownload對(duì)象,則對(duì)象的代理接收一個(gè)connection:canAuthenticateAgainstProtectionSpace: (或者 download:canAuthenticateAgainstProtectionSpace:) 消息。這允許代理對(duì)象在嘗試再次認(rèn)證前分析服務(wù)端的屬性,包括協(xié)議和認(rèn)證方法。如果我們的代理對(duì)象不準(zhǔn)備認(rèn)證服務(wù)端的受保護(hù)空間,則返回NO,且系統(tǒng)嘗試使用用戶(hù)的keychain的信息進(jìn)行認(rèn)證。
如果NSURLConnection或NSURLDownload的代理對(duì)象沒(méi)有實(shí)現(xiàn)connection:canAuthenticateAgainstProtectionSpace: (或者 download:canAuthenticateAgainstProtectionSpace:)方法,且保護(hù)空間使用客戶(hù)端證書(shū)認(rèn)證或服務(wù)端信任認(rèn)證,則系統(tǒng)假設(shè)我們返回NO。而對(duì)象其它所有類(lèi)型,系統(tǒng)都返回YES。
下一步,如果我們的代理對(duì)象同意處理認(rèn)證,但是沒(méi)有有效的證書(shū)(不管是作為請(qǐng)求URL的一部分或者在NSURLCredentialStorage中共享),則代理以收到以下消息:
URLSession:didReceiveChallenge:completionHandler:2. URLSession:task:didReceiveChallenge:completionHandler:3. connection:didReceiveAuthenticationChallenge:4. download:didReceiveAuthenticationChallenge:
為了讓連接能夠繼續(xù),則代理對(duì)象有三種選擇:
提供認(rèn)證證書(shū)
嘗試在沒(méi)有證書(shū)的情況下繼續(xù)
取消認(rèn)證查詢(xún)
為了確保操作的正確流程,傳遞給這些方法的NSURLAuthenticationChallenge實(shí)例會(huì)包含一些信息,包括是什么觸發(fā)了認(rèn)證查詢(xún)、查詢(xún)的嘗試次數(shù)、任何先前嘗試的證書(shū)、請(qǐng)求證書(shū)的NSURLProtectionSpace對(duì)象,及查詢(xún)的發(fā)送者。
如果認(rèn)證請(qǐng)求事先嘗試認(rèn)證且失敗了(如用戶(hù)在服務(wù)端修改了密碼),我們可以通過(guò)在認(rèn)證請(qǐng)求調(diào)用proposedCredential來(lái)獲取嘗試憑據(jù)。代理可以使用這些證書(shū)來(lái)填充一個(gè)顯示給用戶(hù)的話(huà)框。
調(diào)用認(rèn)證請(qǐng)求的previousFailureCount可以返回身份驗(yàn)證嘗試次數(shù),這些嘗試包括不同認(rèn)證協(xié)議的嘗試請(qǐng)求。代理可以將這些方法提供給用戶(hù),以確定先前提供的證書(shū)是否失敗,或限制最大認(rèn)證嘗試次數(shù)。
響應(yīng)認(rèn)證請(qǐng)求
前面說(shuō)過(guò)三種響應(yīng)我們響應(yīng)connection:didReceiveAuthenticationChallenge:代理方法的方式,我們將逐一介紹:
提供證書(shū)
為了進(jìn)行認(rèn)證,程序需要使用服務(wù)端期望的認(rèn)證信息創(chuàng)建一個(gè)NSURLCredential對(duì)象。我們可以調(diào)用authenticationMethod來(lái)確定服務(wù)端的認(rèn)證方法,這個(gè)認(rèn)證方法是在提供的認(rèn)證請(qǐng)求的保護(hù)空間中。NSURLCredential支持一些方法:
HTTP基本認(rèn)證(NSURLAuthenticationMethodHTTPBasic):需要用戶(hù)名和密碼。提示用戶(hù)輸入必要信息并使用credentialWithUser:password:persistence:方法創(chuàng)建一個(gè)NSURLCredential對(duì)象。
HTTP數(shù)字認(rèn)證(NSURLAuthenticationMethodHTTPDigest):類(lèi)似于基本認(rèn)證,需要用戶(hù)名和密碼。提示用戶(hù)輸入必要信息并使用credentialWithUser:password:persistence:方法創(chuàng)建一個(gè)NSURLCredential對(duì)象。
客戶(hù)端證書(shū)認(rèn)證(NSURLAuthenticationMethodClientCertificate): 需要系統(tǒng)標(biāo)識(shí)和所有服務(wù)端認(rèn)證所需要的證書(shū)。然后使用credentialWithIdentity:certificates:persistence:來(lái)創(chuàng)建一個(gè)NSURLCredential對(duì)象。
服務(wù)端信任認(rèn)證(NSURLAuthenticationMethodServerTrust)需要一個(gè)由認(rèn)證請(qǐng)求的保護(hù)空間提供的信任。使用credentialForTrust:來(lái)創(chuàng)建一個(gè)NSURLCredential對(duì)象。
在創(chuàng)建NSURLCredential對(duì)象后
對(duì)于NSURLSession,使用提供的完成處理block將該對(duì)象傳遞給認(rèn)證請(qǐng)求發(fā)送者
對(duì)于NSURLConnection和NSURLDownload,使用useCredential:forAuthenticationChallenge:方法將對(duì)象傳遞給認(rèn)證請(qǐng)求發(fā)送者。
嘗試在沒(méi)有證書(shū)的情況下繼續(xù)
如果代理選擇不提供證書(shū),可以嘗試?yán)^續(xù)操作:
對(duì)于NSURLSession,傳遞下面的值給完成處理block: NSURLSessionAuthChallengePerformDefaultHandling:處理請(qǐng)求。盡管代理沒(méi)有提供代理方法來(lái)處理認(rèn)證請(qǐng)求 NSURLSessionAuthChallengeRejectProtectionSpace:拒絕請(qǐng)求。依賴(lài)于服務(wù)端響應(yīng)允許的認(rèn)證類(lèi)型,URL加載類(lèi)可能多次調(diào)用這個(gè)代理方法。
對(duì)于NSURLConnection和NSURLDownload,在[challenge sender]中調(diào)用continueWithoutCredentialsForAuthenticationChallenge:。
依賴(lài)于協(xié)議的實(shí)現(xiàn),這種處理方法可能會(huì)導(dǎo)致連接失敗而以送connectionDidFailWithError:消息,或者返回可選的不需要認(rèn)證的URL內(nèi)容。
取消連接
代理可以選擇取消認(rèn)證請(qǐng)求
對(duì)于NSURLSession,傳遞NSURLSessionAuthChallengeCancelAuthenticationChallenge給完成處理block
對(duì)于NSURLConnection和NSURLDownload,在[challenge sender]中調(diào)用cancelAuthenticationChallenge:。代理接收connection:didCancelAuthenticationChallenge:消息,以提供用戶(hù)反饋的機(jī)會(huì)。
下面的代碼演示了使用用戶(hù)名和密碼創(chuàng)建NSURLCredential對(duì)象來(lái)響應(yīng)認(rèn)證請(qǐng)求
-(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if ([challenge previousFailureCount] == 0)
{
NSURLCredential *newCredential;
newCredential = [NSURLCredential credentialWithUser:[self preferencesName] password:[self preferencesPassword] persistence:NSURLCredentialPersistenceNone];
[[challenge sender] useCredential:newCredential forAuthenticationChallenge:challenge];
}
else
{? ? ? ? ? [[challenge sender] cancelAuthenticationChallenge:challenge];? ? ? ? ? // inform the user that the user name and password? ? ? ? ? // in the preferences are incorrect? ? ? ? [self showPreferencesCredentialsAreIncorrectPanel:self];? ? ? ? }
}
如果代理沒(méi)有實(shí)現(xiàn)connection:didReceiveAuthenticationChallenge:,而請(qǐng)求需要認(rèn)證,則有效的證書(shū)必須位于URL證書(shū)存儲(chǔ)中或作為請(qǐng)求URL的一部分。如果證書(shū)無(wú)效或者認(rèn)證失敗,則底層實(shí)現(xiàn)會(huì)發(fā)送一個(gè)continueWithoutCredentialForAuthenticationChallenge:消息。
執(zhí)行自定義TLS鏈驗(yàn)證
在NSURL系統(tǒng)的AIP中,TLS鏈驗(yàn)證由應(yīng)用的認(rèn)證代理方法來(lái)處理,但它不是提供證書(shū)給服務(wù)端以驗(yàn)證用戶(hù),而是在TLS握手的過(guò)程中校驗(yàn)服務(wù)端提供的證書(shū),然后再告訴URL加載系統(tǒng)是否應(yīng)該接受還是拒絕這些證書(shū)。
如果需要以非標(biāo)準(zhǔn)的方法(如接收一個(gè)指定的自標(biāo)識(shí)的證書(shū)用于測(cè)試)來(lái)執(zhí)行鏈驗(yàn)證,則應(yīng)用必須如下處理:
對(duì)于NSURLSession,實(shí)現(xiàn)URLSession:didReceiveChallenge:completionHandler:和URLSession:task:didReceiveChallenge:completionHandler:代理方法。如果實(shí)現(xiàn)了兩者,由會(huì)話(huà)級(jí)別的方法負(fù)責(zé)處理認(rèn)證。
對(duì)于NSURLConnection和NSURLDownload,實(shí)現(xiàn)connection:canAuthenticateAgainstProtectionSpace:和download:canAuthenticateAgainstProtectionSpace:方法,如果保護(hù)空間有一個(gè)NSURLAuthenticationMethodServerTrust類(lèi)型的認(rèn)證,則返回YES。然后,實(shí)現(xiàn)connection:didReceiveAuthenticationChallenge:或download:didReceiveAuthenticationChallenge:方法來(lái)處理認(rèn)證。
在認(rèn)證處理代理方法中,我們需要確認(rèn)認(rèn)證保護(hù)空間是否有NSURLAuthenticationMethodServerTrust類(lèi)型的認(rèn)證,如果有,則從保護(hù)空間獲取serverTrust信息。