Objective-C报”NSURLErrorUserAuthenticationRequired”异常的原因和解决办法

  • Post category:IOS

一、NSURLErrorUserAuthenticationRequired异常原因

NSURLErrorUserAuthenticationRequired异常表示需要用户验证才能继续进行请求。该异常一般是由以下两种情况引起:

  1. 流量被用户验证器拦截:当要访问的URL需要用户验证时,iOS将会弹出一个窗口提示用户输入用户名和密码,如果用户未输入或输入的信息不正确,将会引发该异常。

  2. 输入的证书无效:如果使用的证书无效,例如不是签名证书或已过期,同样会发生该异常。

二、NSURLErrorUserAuthenticationRequired异常解决办法

针对第一种情况,我们需要根据应用的需求向用户请求相应的用户验证信息并进行网络请求。例如,在以下示例中,我们通过NSURLSession从服务器获取用户名和密码,再将验证信息添加到请求头中,以达到用户验证的目的,并避免抛出NSURLErrorUserAuthenticationRequired异常:

// 创建基本NSURLRequest
NSURL *url = [NSURL URLWithString:@"https://example.com/path"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

// 从服务器获取用户名和密码
NSString *username = @"your-username";
NSString *password = @"your-password";

// 添加Authorization到请求头
NSString *authStr = [NSString stringWithFormat:@"%@:%@", username, password];
NSData *authData = [authStr dataUsingEncoding:NSUTF8StringEncoding];
NSString *base64AuthData = [authData base64EncodedStringWithOptions:0];
NSString *authValue = [NSString stringWithFormat:@"Basic %@", base64AuthData];
[request addValue:authValue forHTTPHeaderField:@"Authorization"];

// 发起请求
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    // 处理响应
}];

[task resume];

针对第二种情况,我们需要验证证书的有效性。例如,在以下示例中,我们自定义NSURLSessionDelegate,并重写其URLSession:didReceiveChallenge:completionHandler:方法,在其中验证服务器提供的证书是否有效,并处理验证结果,避免抛出NSURLErrorUserAuthenticationRequired异常:

@interface CustomSessionDelegate : NSObject <NSURLSessionDelegate>

@end

@implementation CustomSessionDelegate

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
    // 验证服务器的SSL证书是否有效
    if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) {
        SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
        if (serverTrust) {
            // 注意:这里需要替换成自己的证书根目录
            NSString *rootCertificateFilename = @"my-root-certificate.crt";
            NSString *rootCertificatePath = [[NSBundle mainBundle] pathForResource:rootCertificateFilename ofType:nil];
            NSData *rootCertificateData = [NSData dataWithContentsOfFile:rootCertificatePath];

            // 创建证书根列表
            CFArrayRef rootCertificates = NULL;
            if (rootCertificateData) {
                SecCertificateRef rootCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)rootCertificateData);
                if (rootCertificate) {
                    const void *rootCertificatesArray[] = { rootCertificate };
                    rootCertificates = CFArrayCreate(NULL, rootCertificatesArray, 1, &kCFTypeArrayCallBacks);
                    CFRelease(rootCertificate);
                }
            }

            // 验证证书有效性
            SecTrustResultType trustResult = kSecTrustResultOtherError;
            SecTrustSetAnchorCertificates(serverTrust, rootCertificates);
            SecTrustEvaluate(serverTrust, &trustResult);

            // 处理验证结果
            if (trustResult == kSecTrustResultUnspecified || trustResult == kSecTrustResultProceed) {
                NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
                completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
                return;
            }
        }
    }

    completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, NULL);
}

@end

我们在创建NSURLSession时设置自定义的delegate,代码如下:

NSURL *url = [NSURL URLWithString:@"https://example.com/path"];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:[[CustomSessionDelegate alloc] init] delegateQueue:[NSOperationQueue mainQueue]];
NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    // 处理响应
}];

[task resume];