sendAsynchronousRequest:queue:completionHandler:使用Delegate方法

时间:2014-07-23 00:30:12

标签: ios objective-c cocoa-touch nsurlconnection

我有一台安装了自签名SSL证书的服务器。但是,一旦我调用以下方法,它就不会得到任何响应。一旦我将URL更改回http,它就可以工作。

- (void)getAccountInfoWithCompletion:(void (^)(NSDictionary *json_response, NSError *error))completion
{
    NSURLRequest *request = [NSURLRequest requestWithURL:
                             [NSURL URLWithString:
                              [NSString stringWithFormat:@"%@/api/account/%d/get_info", BASE_HOST_URL_IP, [self getUserID]]
                            ]];

    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
        if (error)
        {
            if (completion)
            {
                //completion(@"error", error);
            }
        } else {
            NSString *response_string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            NSDictionary *json_object = [NSJSONSerialization JSONObjectWithData:[response_string dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];

            if (completion)
            {
                completion(json_object, error);
            }
        }
    }];
}

我代表的原因是我可以在我的应用程序中使用自签名证书。以下教程是我正在使用的,但后来我意识到我无法使用completionHandler方法的委托。我需要保留completionHandler方法。

http://www.cocoanetics.com/2010/12/nsurlconnection-with-self-signed-certificates/

如果从SSL网站收到回复,我该怎么办?

2 个答案:

答案 0 :(得分:3)

在您描述的情况下,您(几乎)必须使用委托。

这里发生的是sendAsynchronousRequest:queue:completion:使用URL加载系统的默认行为。网址加载系统会看到您自签名的证书,无法对其进行验证,因此无法信任它 - 并且无法连接。您应该看到NSError传入完成处理程序,其中填充了有关该问题的信息。

这在Technote 2232: HTTPS Server Trust Evaluation

中有详细描述

要允许自签名证书,除非您有办法让您的自签名证书受信任并存储在钥匙串中,否则您无法使用sendAsynchronousRequest:queue:completion: - 在iOS上,这仅适用于受管设备。对于测试,仅用于测试,您可以使用private Apple API来更改默认信任行为。

对于生产代码,您必须实现NSURLConnectionDelegate,该NSURLConnectionDelegate处理评估服务器提供的凭据并允许您使用自签名证书。这也在技术说明2232中进行了描述(在高级别)。如果你没有正确实现这一点,你可能会在你的应用程序中创建一个安全漏洞 - 这会很糟糕,mmmmmk?

我不建议您遵循您引用的Cocoanetics帖子的指导。材料过时且质量有问题。请参阅NSURLConnectionDelegate的文档和提及的Technote 2232。如果您想了解有关移动应用程序的传输级安全性的更多信息,可以使用plenty of resources

如果您仍然希望使用自签名证书,则可以实施SSL公钥固定,以使远程(自签名)公钥与存储在应用程序中的已知本地值相匹配。这比尝试仅匹配主机名要好得多。一些示例代码可以帮助您入门here

答案 1 :(得分:2)

ViewController.h:

@interface ViewController : UIViewController <NSURLSessionDelegate>

@end

ViewController.m:

- (void)getAccountInfoWithCompletion:(void (^)(NSDictionary *json_response, NSError *error))completion
{
    NSURLRequest *request = [NSURLRequest requestWithURL:
                             [NSURL URLWithString:
                              [NSString stringWithFormat:@"%@/api/account/%d/get_info", BASE_HOST_URL_IP, [self getUserID]]
                              ]];

    NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration ephemeralSessionConfiguration];
    defaultConfigObject.requestCachePolicy = NSURLRequestReloadIgnoringLocalAndRemoteCacheData;

    NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue: [NSOperationQueue mainQueue]];

    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;

    NSURLSessionDataTask *dataTask = [defaultSession dataTaskWithRequest:request
                                                       completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
    {
        [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;

        if (error == nil && data != nil)
        {
            NSString *response_string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            NSDictionary *json_object = [NSJSONSerialization JSONObjectWithData:[response_string dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];

            if (completion)
            {
                completion(json_object, error);
            }
        }
    }];
    [dataTask resume];
}

新的美丽委托方法让我们替换NSURLConnection的sendAsynchronousRequest方法(无法处理SSL)

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
{
    NSString *host = challenge.protectionSpace.host;

    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
    {
        if ([host rangeOfString:@"yourHost.net"].location != NSNotFound)
        {
            completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
        }
        else
        {
            completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge,nil);
        }
    }
}