为什么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是每个会话或每个任务,我检查了我传递给completionHandler
的NSURLCredential
,并且我发现它具有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下面的传输(套接字)层,但基金会感谢在NSURLConnection
和NSURLSession
等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。