在使用后台会话配置时,我发现了有关NSURLSession的有线事。 我们在与服务器联系时使用自签名证书,这是一个工具:
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
if (self.taskDidReceiveAuthenticationChallenge) {
disposition = self.taskDidReceiveAuthenticationChallenge(session, task, challenge, &credential);
} else {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
disposition = NSURLSessionAuthChallengeUseCredential;
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
NSLog(@"ServerTrust:%@", task.originalRequest.URL);
} else if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate]) {
if (self.clientCertCredential && [challenge previousFailureCount] == 0) {
credential = self.clientCertCredential;
disposition = NSURLSessionAuthChallengeUseCredential;
NSLog(@"ClientCert:%@", task.originalRequest.URL);
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}
使用defaultSessionConfiguration时,它工作正常,但是当我将会话配置更改为后台会话配置时,将在循环中调用此委托方法,将不会调用其他任何委托方法,并且此请求将永远不会完成。
这是控制台输出:
2014-08-11 15:36:01.204 OneBox [1736:a413] ServerTrust:https://demo.mycompany.com/api/v1/files/351/1/9cc69106455d11599a08ed978fbdbe1d/contents 2014-08-11 15:36:01.232 OneBox [1736:1413] ClientCert:https://demo.mycompany.com/api/v1/files/351/1/9cc69106455d11599a08ed978fbdbe1d/contents 2014-08-11 15:36:02.068 OneBox [1736:8c03] ServerTrust:https://demo.mycompany.com/api/v1/files/351/1/9cc69106455d11599a08ed978fbdbe1d/contents 2014-08-11 15:36:02.076 OneBox [1736:1413] ClientCert:https://demo.mycompany.com/api/v1/files/351/1/9cc69106455d11599a08ed978fbdbe1d/contents 2014-08-11 15:36:12.728 OneBox [1736:1413] ServerTrust:https://demo.mycompany.com/api/v1/files/351/1/9cc69106455d11599a08ed978fbdbe1d/contents 2014-08-11 15:36:12.735 OneBox [1736:1413] ClientCert:https://demo.mycompany.com/api/v1/files/351/1/9cc69106455d11599a08ed978fbdbe1d/contents
答案 0 :(得分:1)
好的,我知道这是一个古老的问题,但我也在这个问题上一直在敲打墙壁,所以希望这会对某人有所帮助,这是我解决问题的方法。 公平地说,我的“解决方案”对我来说更像是一个错综复杂的解决方案,而不是一个合适的解决方案,但至少它是有效的。
简短回答是让它发挥作用的关键是使用NSURLProtectionSpace
为所有会话设置默认永久凭据。这可以防止在呈现NSURLAuthenticationMethodClientCertificate
类型的质询时调用委托。
长答案如下所示。
在您的代码中,这不起作用:
credential = self.clientCertCredential;
disposition = NSURLSessionAuthChallengeUseCredential;
//
// Redacted for clarity
//
completionHandler(disposition, credential);
因为在后台会话中,代理人无法访问self.clientCertCredential
(上帝只知道原因)。
但是我发现如果您之前在NSURLProtectionSpace
中定义了默认凭据,后台会话将不会尝试调用该委托。
请抓住所有else if
块,然后执行以下操作:
NSURLProtectionSpace *space = [NSURLProtectionSpace
initWithHost:@"your_address"
port:your_port
protocol:@"https"
realm:@"your_realm"
authenticationMethod:NSURLAuthenticationMethodClientCertificate];
[[NSURLCredentialStorage sharedCredentialStorage]
setDefaultCredential:self.clientCertCredential
forProtectionSpace:space];
如果主机,端口和域参数与服务器的参数完全匹配,那么当质询提交给后台会话时, challenge.protectionSpace
将自动找到默认凭据。
为了使其工作,在尝试使用后台会话发出任何请求之前,需要执行此代码。例如,只要将客户端证书加载到self.clientCertCredential
,就可以执行此操作。
但要小心!这里还有一个细微之处。每当您这样做时,请确保使用持久性选项NSURLCredentialPersistencePermanent
加载证书。否则它将无法工作。
最后一点。根据您的使用情况,使用此hack的缺点是,如果多个NSURLProtectionSpaces
,您可能会发现自己拥有一堆永久持久的凭据。在为defaultCredential
课程设置NSURLCredentialStorage
后,您可能需要做一些内务管理。这超出了本答案的范围,但是该类有一些方便的方法,例如-removeCredential:forProtectionSpace:
,这里记录了https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSURLCredentialStorage_Class/