Ios 为什么HTTPS NSURLSession连接在每个域中只被质询一次?

Ios 为什么HTTPS NSURLSession连接在每个域中只被质询一次?,ios,cocoa-touch,https,nsurlsession,Ios,Cocoa Touch,Https,Nsurlsession,当通过HTTPS连接到服务器时,我实现了nsurlsessionelegate方法URLSession:didReceiveChallenge:completionHandler:,以实现一些自定义功能 问题在于,只有在第一次发出请求时才会调用此委托方法(后续请求不会调用此方法)。我的自定义功能要求为每个请求调用委托方法 下面是一个例子: - (IBAction)reload:(id)sender { NSURLSession *session = [NSURLSession sessi

当通过HTTPS连接到服务器时,我实现了
nsurlsessionelegate
方法
URLSession:didReceiveChallenge:completionHandler:
,以实现一些自定义功能

问题在于,只有在第一次发出请求时才会调用此委托方法(后续请求不会调用此方法)。我的自定义功能要求为每个请求调用委托方法

下面是一个例子:

- (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
(不可变),这似乎是正确的

我还检查了
[nsurlCredentialsStorage 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]);
}
编辑


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

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

从较高的层次描述HTTP信任评估,并详细介绍如何实现它

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

这就像看某人的驾照或护照,然后决定他们是否是他们所说的人

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

这实际上发生在HTTP下面的传输(套接字)层上,但是基金会欣然地暴露了API中的这个更高的级别,比如<代码> NSURLCONNECT/<代码>和NSURLSECTION/COMP> >作为给定保护空间的证书挑战。如果保护空间(主机)或服务器凭据发生更改,则会发生新的凭据质询。这将反过来促进信任评估

由于SSL/TLS是一种套接字级安全措施,实际工作远远低于在代码> > SeCurrase安全套接字框架内的基础URL加载系统。代码>安全传输维护自己的每进程TLS会话缓存。这是您必须绕开的层,以获得所需的行为-您需要清除每个连接的TLS会话缓存,或强制
SecureTransport
忽略进程的会话缓存

更详细地描述
SecureTransport
会话缓存,并可能提供一些绕过TLS缓存的有趣选项(即与DNS混淆)

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

TL;博士 “那么,为什么挑战只有一次?” 第一次TLS信任评估的结果由会话缓存中的
SecureTransport
缓存

目前没有办法控制这种特殊的行为


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

Great information@quellish,正如您所说,目前似乎无法正确绕过TLS会话缓存,因此我提交了一份错误报告19072802@quellish在文档中(您已发布)我发现NSURLSession维护自己的TLS会话缓存。我还注意到,在转移到PROD环境后,我开始发现确实收到挑战的问题并没有被调用,但在PRE-PORD上,我一直都在遇到。我觉得这可能与后端有关。是吗?你找到解决问题的办法了吗?当我搬到PROD环境时,我开始遇到同样的问题。在试生产前,我一直受到挑战。在这一点上,对我来说,它似乎是与后端有关。我最近测试了这个,它似乎是固定的(它总是要求认证)。如果我找到了我的示例项目,我会把它给你。在阅读了Apple文档后,我发现会话级挑战是由服务器负责的。需要查看后端配置/设置。如果有兴趣,请在这里查找“会话级别”。因此,通过在nginx中禁用ssl会话缓存,我的问题得到了解决。