2

在使用后台会话配置时,我发现了一个关于 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/内容 2014-08-11 15:36:12.735 OneBox[1736:1413] ClientCert: https://demo.mycompany.com/api/v1/files/351/1/9cc69106455d11599a08ed978fbdbe1d/contents

4

1 回答 1

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];

如果hostportrealm参数与您的服务器的参数完全匹配,那么当向后台会话提出质询时,challenge.protectionSpace将自动找到默认凭据。

为了让它工作,在您尝试使用后台会话发出任何请求之前,需要执行此代码。例如,您可以在将客户端证书加载到其中时执行此操作self.clientCertCredential

但要小心!!!这里还有一个微妙之处。无论何时这样做,请确保使用持久性选项加载证书NSURLCredentialPersistencePermanent。否则它将无法正常工作。

最后一点。根据您的用例,使用此 hack 的缺点是,如果有多个NSURLProtectionSpaces. 然后,您可能需要defaultCredentialNSURLCredentialStorage课程设置后做一些家务。这超出了此答案的范围,但该类有一些方便的方法,例如-removeCredential:forProtectionSpace:此处记录的https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSURLCredentialStorage_Class/

于 2016-02-08T17:46:24.850 回答