通过HTTPS连接到服务器时,我实施NSURLSessionDelegate
方法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是每个会话或每个任务,我检查了NSURLCredential
我传递给completionHandler
,我发现它persistence
NSURLCredentialPersistenceForSession
}(这是不可变的),这似乎是正确的。
我还检查了[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
答案 0 :(得分:10)
您在此处真正要做的是评估每个请求的服务器凭据的信任度。
Technote 2232: HTTPS Server Trust Evaluation描述了高级别的HTTP信任评估,并详细介绍了如何实现它。
使用SSL / TLS连接主机时,主机会显示一组加密凭据。您的应用程序(可能直接是用户)必须评估这些凭据并确定是否可以信任。
这就像看某人的驾驶执照或护照一样,并决定他们是谁,他们是谁。
想象一下,如果你为他们说的每个单词看一次某人的身份证明。这会得到tedius!除非这个人改变了,或者他们的身份改变了,否则没有意义。如果服务器或其凭据发生更改,iOS将执行信任评估。
这实际上发生在HTTP下面的传输(套接字)层,但基金会感谢在NSURLConnection
和NSURLSession
等API中将此更高层暴露为给定保护空间的凭证挑战。如果保护空间(主机)或服务器凭据发生更改,则会发生新的凭据质询。这将反过来促进信任评估。
由于SSL / TLS是套接字级别的安全措施,因此实际工作远远低于安全套接字框架SecureTransport
内的基础URL加载系统。 SecureTransport
维护自己的每进程TLS会话缓存。这是您必须规避的层以获取您正在寻找的行为 - 您需要清除每个连接的TLS会话缓存,或强制SecureTransport
忽略您的流程的会话缓存。
Technical Q&A 1727: TLS Session Cache更详细地描述了SecureTransport
会话缓存,并且可能提供一些有趣的选项来绕过TLS缓存(即弄乱DNS)。
此时没有用于清除或修改SecureTransport
TLS会话缓存的API。您可以提交请求此功能的雷达。
"那么为什么挑战只进行一次?"
第一个TLS信任评估的结果由SecureTransport
缓存在会话缓存中。
此时无法控制该特定行为。
您可以尝试使用其他一些HTTPS库或框架(例如OpenSSL),YMMV。