首页 > 解决方案 > NSURLSession 完成处理程序意味着如果 didReceiveChallenge 被自动调用

问题描述

有人可以解释我是否didReceiveChallenge在我向 https 服务器发出请求后自动调用NSURLSession,如果完成处理程序在完成后调用一些内部方法,didReceiveChallenge以及我如何访问这个完成处理程序?委托方法有以下公司:

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler

[编辑]

通常我看到这个基本实现的方法:

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
{
  if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
  {
    if([challenge.protectionSpace.host
        isEqualToString:@"google.it"])
    {
      NSURLCredential *credential = [NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust];
      completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
    }
    else
      completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
  }

}

标签: iosnsurlsessionnsurlrequest

解决方案


在进一步讨论之前,我应该首先指出,这样做几乎总是一个错误。您可以从LetsEncrypt等各种团体获得免费的 TLS 证书。因此,除非您有一些非常不寻常的用例(例如需要为未连接到公共 Internet 的设备提供信任,通过链接本地网络进行通信),否则您最好要这样做,而只是安装测试服务器上的真实 TLS 证书。

照这样说....

URLSession:didReceiveChallenge:completionHandler:每当操作系统需要额外确认时,都会在会话的委托(如果非零)上调用该方法。可能不会为每个请求调用它,但通常会调用它。由于服务器信任评估,每个 https 请求、周期都会调用它。

您上面的代码可能会让您失望,因为在保护空间不是服务器信任(例如代理身份验证、HTTP 基本/摘要身份验证等)的情况下,您没有要求默认处理,这意味着网络机器只是坐在那里等你告诉它该做什么,而没有注意到它传递给你的块在方法返回时已被释放,因此永远不会被调用。

你应该做这样的事情:

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
{
  if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
  {
    if([challenge.protectionSpace.host isEqualToString:@"google.it"])
    {
      if (/* Manually verify the certificate here in some way */) {
        NSURLCredential *credential = [NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust];
        completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
      } else {
        // Evaluation failed.  Reject the certificate.
        completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
      }
      // Do not fall through for either case above.
      return;
    }
  }
  completionHandler(NSURLSes NSURLSessionAuthChallengePerformDefaultHandling, nil);
}

此外,您发布的代码非常不安全,因为您没有做任何事情来验证证书。有关如何执行此步骤的更多信息,请参阅Apple 开发人员文档中的正确覆盖 TLS 链验证,但通常您可以通过以下任一方式执行此操作

  • 提供用于签署假证书的根证书(不带密钥)的副本,然后将该根证书添加到一组有效的根证书中,然后重新评估证书。
  • 提供伪造证书的公钥副本,并断言它与预期的密钥匹配。

无论采用哪种方法,您都应该回退到默认处理,这样如果您将自签名证书替换为真实证书,它将“正常工作”。


推荐阅读