如何让 IOS 应用信任本地主机进行开发 API

How to get IOS app to trust localhost for development API

将 Visual Studio 用于 Mac,我创建了一个 RESFUL API(从 API 项目模板开发),开箱即用的输出非常简单JSON 文件(即 ["value1","value2"])位于以下 url:https://localhost:5001/api/values

因此,虽然 运行 Visual Studio 上的 API 在后台运行,但我也在同一台计算机上 运行 XCode;因为我正在开发一个需要连接到 API url 并输出预期的 JSON 响应的应用程序。

问题是它由于信任错误而不断失败:"TIC SSL Trust Error...NSURLSession/NSURLConnection HTTP Load failed"。

根据我的研究,我认为我需要将本地主机的自签名证书安装到我的 XCode 模拟器上,以便它允许应用程序信任 url。如何访问此本地主机证书?

或者这是正确的方法吗?

产生信任错误的代码:

// Specify the base url...
static NSString *const API_URL = @"https://localhost:5001";
// Specify completed url ...
NSString *urlAppend = [NSString stringWithFormat:@"/api/values"];
NSString *urlStr = [[[NSString alloc] initWithString:API_URL]
stringByAppendingString:urlAppend];        

// Specify the URL object that can return JSON, XML, etc....
NSURL *url = [[NSURL alloc] initWithString:urlStr];

// Create an NSURLSessionDataTask that does the GET request at the specified URL (using NSURLSession shared session)...
NSURLSessionDataTask *task = [[NSURLSession sharedSession]
              dataTaskWithURL:url
              completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

                  // Handle response (output respnose to log window))...
                  NSLog(@"%@", [output initWithData:data  encoding:(NSASCIIStringEncoding)] ? : @"no data");
              }];

// Start task ....
[task resume];

您可以尝试这样做:在发出任何 HTTP 请求的每个 ViewController 的顶部,就在导入部分下方,添加以下代码:

@interface NSURLRequest (DummyInterface)
+ (BOOL)allowsAnyHTTPSCertificateForHost:(NSString*)host;
+ (void)setAllowsAnyHTTPSCertificate:(BOOL)allow forHost:(NSString*)host;
@end

然后,在你发出请求的地方,调用这个:

[request setHTTPBody:data];

[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[url host]];

NSError *error;
NSHTTPURLResponse  *response = nil;

NSData *data2 = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

解决此问题的最佳方法是 Perform Manual Server Trust Authentication for the url of the localhost development server (running in the background). This is done by utilizing the URLSession:didReceiveChallenge:completionHandler: delegate method of your NSURLSessionDelegate;这将使您有机会匹配本地主机 url 并在会话数据任务开始时手动信任它(在它因不可信 self-signed-certificate 而拒绝之前)。

方法如下:

第 1 步:将 NSURLSession 操作包装在 class 中,比如 "Networking",它实现了 NSURLSessionDelegate 协议;并添加手动信任本地主机 url(指定为 'DEV_URL')的委托方法。

// Networking.h
#import <Foundation/Foundation.h>

@interface Networking : NSObject <NSURLSessionDelegate>
- (void) fetchContentsOfUrl:(NSURL *)url
                 completion:(void (^)(NSData *data, NSError *error)) completionHandler;
@end
// Networking.m
#import "Networking.h"

static NSString *const DEV_URL = @"https://localhost:5001";
@implementation Networking
- (void) fetchContentsOfUrl:(NSURL *)url
                 completion:(void (^)(NSData * _Nullable, NSError * _Nullable))completionHandler {
    NSURLSession *dataSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];

    NSURLSessionDataTask *dataTask = [dataSession
                                      dataTaskWithURL:url
                                      completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

                                          if (completionHandler == nil) {
                                              return;
                                          }

                                          if (error){
                                              completionHandler(nil, error);
                                              return;
                                          }

                                          completionHandler(data, nil);
                                      }];

    [dataTask resume];
}

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

    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        NSURL* baseURL = [NSURL URLWithString:DEV_URL];
        if ([challenge.protectionSpace.host isEqualToString:baseURL.host]) {
            NSLog(@"trusting connection to host %@", challenge.protectionSpace.host);
            completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
        } else {
            NSLog(@"Not trusting connection to host %@", challenge.protectionSpace.host);
            completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
        }
    }
    [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}
@end

第 2 步:用 Networking class 的实例化和调用 fetchContentsOfUrl:completion: 调用 NSURLSessionDataTask 的方法替换您的原始代码,如下所示:

    Networking *networker = [[Networking alloc] init];
    [networker fetchContentsOfUrl:url completion:^(NSData *data, NSError *error) {        
        if (error == nil) {
            NSString *output = [NSString alloc];
            NSLog(@"%@", [output initWithData:data  encoding:(NSASCIIStringEncoding)] ? : @"no data");
        }
    }];

大功告成!!