NSURLSessionTask身份验证质询completionHandler和NSURLAuthenticationChallenge客户端

时间:2014-12-22 13:52:40

标签: ios nsurlsession nsurlprotocol nsurlsessiondatatask

我正在实施自定义NSURLProtocol,并且内部希望将NSURLSession用于内部网络的数据任务,而不是NSURLConnection

我遇到了一个有趣的问题,并对NSURLSession / NSURLSessionTask的质询处理程序的内部实现感到疑惑。

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
                            didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge 
                              completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler;

这里我基本上提供了两个不同的挑战处理程序,一个是completionHandler块,它提供了处理挑战的所有必要信息,但也有遗留的NSURLAuthenticationChallenge.client有方法与completionHandler信息选项几乎一一对应。

由于我正在开发一个协议,并且希望将调用API的URL加载系统向上传递某些身份验证问题,我需要使用NSURLSession客户端方法:

- (void)URLProtocol:(NSURLProtocol *)protocol didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;

我的问题是completionHandlerNSURLAuthenticationChallenge.client的内部实现是否相同,如果是这样,我是否可以跳过调用委托方法中的完成处理程序,期望URL加载系统会调用相应的NSURLAuthenticationChallenge.client方法吗?

1 个答案:

答案 0 :(得分:6)

要回答我自己的问题,答案是否定的。此外,Apple提供的挑战发送者没有实现整个NSURLAuthenticationChallengeSender协议,因此当客户端尝试响应挑战时崩溃:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFURLSessionConnection performDefaultHandlingForAuthenticationChallenge:]: unrecognized selector sent to instance 0x7ff06d958410'

我的解决方案是创建一个包装器:

@interface CPURLSessionChallengeSender : NSObject <NSURLAuthenticationChallengeSender>

- (instancetype)initWithSessionCompletionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler;

@end

@implementation CPURLSessionChallengeSender
{
    void (^_sessionCompletionHandler)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential);
}

- (instancetype)initWithSessionCompletionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
    self = [super init];

    if(self)
    {
        _sessionCompletionHandler = [completionHandler copy];
    }

    return self;
}

- (void)useCredential:(NSURLCredential *)credential forAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    _sessionCompletionHandler(NSURLSessionAuthChallengeUseCredential, credential);
}

- (void)continueWithoutCredentialForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    _sessionCompletionHandler(NSURLSessionAuthChallengeUseCredential, nil);
}

- (void)cancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
{
    _sessionCompletionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
}

- (void)performDefaultHandlingForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    _sessionCompletionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
}

- (void)rejectProtectionSpaceAndContinueWithChallenge:(NSURLAuthenticationChallenge *)challenge
{
    _sessionCompletionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
}

@end

我使用我的包装发件人替换挑战对象:

NSURLAuthenticationChallenge* challengeWrapper = [[NSURLAuthenticationChallenge alloc] initWithAuthenticationChallenge:challenge sender:[[CPURLSessionChallengeSender alloc] initWithSessionCompletionHandler:completionHandler]];
[self.client URLProtocol:self didReceiveAuthenticationChallenge:challengeWrapper];