为什么HTTPS NSURLSession连接每个域只挑战一次?

当通过HTTPS连接到服务器时,我实现了NSURLSessionDelegate方法URLSession:didReceiveChallenge:completionHandler:来实现一些自定义function。

问题是这个委托方法只在第一次发出请求时被调用(后续请求不会调用此方法)。 我的自定义function要求为每个请求调用委托方法。

这是一个例子:

 - (IBAction)reload:(id)sender { NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration] delegate:self delegateQueue:nil]; // Note that https://www.example.com is not the site I'm really connecting to. NSURL *URL = [NSURL URLWithString:@"https://www.example.com"]; NSMutableURLRequest *URLRequest = [NSMutableURLRequest requestWithURL:URL]; [[session dataTaskWithRequest:URLRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { // Response received here. }] resume]; } #pragma NSURLSessionDelegate - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { // Called only for the first request, subsequent requests do no invoke this method. completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); } 

由于我希望URLCredential是每个会话或每个任务,我检查了我传递给completionHandlerNSURLCredential ,并且我发现它具有NSURLCredentialPersistenceForSession (这是不可变的)的persistence ,这似乎是正确的。

我还检查了[NSURLCredentialStorage allCredentials]并且它是空的,因此它不会缓存那里的凭据。

我注意到,如果我随后向具有不同域的HTTPS URL发出请求,则会针对该域调用一次挑战,因此它基于每个域。

那么挑战是如何进行的呢?

编辑

切换到NSURLSessionTaskDelegate并使用URLSession:task:didReceiveChallenge:completionHandler:没有任何区别。

 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); } 

编辑 相关问题

编辑由于目前似乎没有办法解决这个问题,我已经提交了一份Apple错误报告:19072802

您在这里要做的是评估每个请求的服务器凭据的信任度。

技术说明2232:HTTPS服务器信任评估描述了高级别的HTTP信任评估,并详细介绍了如何实现它。

使用SSL / TLS连接到主机时,主机会显示一组加密凭据。 您的应用程序(可能是直接用户)必须评估这些凭据并确定是否可以信任。

这就像看某人的驾驶执照或护照一样,并决定他们是谁。

想象一下,如果你为他们说的每个单词看一次某个人的身份。 这会得到tedius! 除非这个人改变了,或者他们的身份发生了变化,否则没有意义。 如果服务器或其凭据发生更改,iOS将执行信任评估。

这实际上发生在HTTP下面的传输(套接字)层,但基金会感谢在NSURLConnectionNSURLSession等API NSURLSession作为给定保护空间的凭证挑战。 如果保护空间(主机)或服务器凭据发生更改,则会发生新的凭据质询。 这将反过来促进信任评估。

由于SSL / TLS是套接字级别的安全措施,因此实际工作远远低于SecureTransport (安全套接字框架)内的Foundation URL加载系统。 SecureTransport维护自己的每进程TLS会话缓存。 这是您必须绕过以获取所需行为的层 – 您需要清除每个连接的TLS会话缓存,或强制SecureTransport忽略您的进程的会话缓存。

技术问答1727:TLS会话缓存更详细地描述了SecureTransport会话缓存,并且可能提供一些有趣的选项来绕过TLS缓存(即弄乱DNS)。

目前,没有用于清除或修改SecureTransport TLS会话缓存的API。 您可以提交请求此function的雷达。

TL; DR;

“那么挑战怎么样呢?” 第一个TLS信任评估的结果由会话缓存中的SecureTransport缓存。

目前还没有办法控制这种特定的行为。

您可以尝试使用其他一些HTTPS库或框架(例如OpenSSL),YMMV。