美文网首页
iOS NSURLAuthenticationChallenge

iOS NSURLAuthenticationChallenge

作者: 天下林子 | 来源:发表于2018-09-27 16:45 被阅读59次

NSURLAuthenticationChallenge

在http请求中某些url访问需要具有权限认证,否则会返回401错误码,这时需要你在请求头中附带授权的用户名,密码;或者使用https协议第一次与服务端建立连接的时候,服务端会发送iOS客户端一个证书,这时我们需要验证服务端证书链(certificate keychain)。
当我们遇到以上情况的时候NSURLSession(在iOS7以后网络请求全部由NSURLSession来完成所以在此以NSURLSession为例)中的一个delegate会被调用:


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

这个代理就是为了处理从服务端传回的NSURLAuthenticationChallenge(认证),在NSURLAuthenticationChallenge的protectionSpace中存放了服务端返回的认证信息,我们需要根据这些信息来创建对应的NSURLCredential(证书/凭证),并且设置对应的NSURLSessionAuthChallengeDisposition(处理方式)来告诉NSURLSession来如何处理处服务端返回的个认证.
在NSURLSessionAuthChallengeDisposition中包含三种类型

NSURLSessionAuthChallengePerformDefaultHandling:默认方式处理
 NSURLSessionAuthChallengeUseCredential:使用指定的证书
 NSURLSessionAuthChallengeCancelAuthenticationChallenge:取消

AFNetworking在代理中做了如下处理:

NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    __block NSURLCredential *credential = nil;

    if (self.sessionDidReceiveAuthenticationChallenge)
    {   // 自定义认证处理方式
        disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
    }
    else
    {   // 默认处理方式
        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
        {
            // 根据AFSecurityPolicy的默认规则判断是否需要新建证书
            if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host])
            {
                credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; // 根据服务器返回的challenge中的protectionSpace中的SecTrustRefwww创建证书
                if (credential) {
                    disposition = NSURLSessionAuthChallengeUseCredential; // 使用创建的证书
                } else {
                    disposition = NSURLSessionAuthChallengePerformDefaultHandling; //使用默认方式
                }
            }
            else
            {
                disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
            }
        }
        else
        {
            disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        }
    }

    if (completionHandler) {
        completionHandler(disposition, credential);
    }



NSURLAuthenticationChallenge 是由服务端返回的权限认证类,中间包含如下信息:


- (NSURLProtectionSpace *)protectionSpace; // 这个函数返回一个类NSURLProtectionSpace,类中描述服务器中希望的认证方式以及协议,主机端口号等信息。
- (NSURLCredential *)proposedCredential; // 建议使用的证书
- (NSInteger)previousFailureCount; // 用户密码输入失败的次数。
- (NSURLResponse *)failureResponse; // 授权失败的响应头的详细信息
- (NSError *)error; // 最后一次授权失败的错误信息


image.png

当客户端发出一个网络请求的时候(图中的第1,2步),服务端会收到来自客户端的请求并作出响应(图中的第3步),这里服务端发现此请求需要输入用户名才能继续,所以客户端返回了一个权限认证,这是客户端中的NSURLSession的delegate会被调用,这时我们可以从challenge中的protectionSpace里的authenticationMethod属性获取到当前服务器需要我们提供什么类型的认证 (在图中authenticationMethod为NSURLAuthenticationMethodHTTPDigest) ,根据不同的类型使用不同的方式创建需要处理的credential然后设置对应的disposition参数,然后通过completionHandler告诉Session如何处理此次的权限认证(其实最后最后session会将此凭证返回给服务端,再由服务端去校验输入用户名密码是否正确,如果正确便继续请求,如果不正确的话会继续返回认证).

示例代码


- (IBAction)URLAuthenticationChallenge:(id)sender {
    
    NSURL *url = [NSURL URLWithString:@"https://kyfw.12306.cn/otn/leftTicket/init"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request];
    [task resume];
    
}


#pragma mark - NSURLSessionDataDelegate

// 只要访问的是HTTPS的路径就会调用
// 该方法的作用就是处理服务器返回的证书, 需要在该方法中告诉系统是否需要安装服务器返回的证书
// NSURLAuthenticationChallenge : 授权质问
//+ 受保护空间
//+ 服务器返回的证书类型

- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
    NSLog(@"++++++++++didReceiveChallenge");
    NSLog(@"____authenticationMethod______%@", challenge.protectionSpace.authenticationMethod);
    
    // 1.从服务器返回的受保护空间中拿到证书的类型
    // 2.判断服务器返回的证书是否是服务器信任的
    
    //AFNetworking中的处理方式
    NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    __block NSURLCredential *credential = nil;
    //判断服务器返回的证书是否是服务器信任的
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        NSLog(@"------->>>>>>是服务器信任的证书");
        // 3.根据服务器返回的受保护空间创建一个证书
        //void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *)
        //代理方法的completionHandler block接收两个参数:
        //第一个参数: 代表如何处理证书
        //第二个参数: 代表需要处理哪个证书
        
        //创建证书
        credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
        
        /*
         disposition:如何处理证书
         NSURLSessionAuthChallengePerformDefaultHandling:默认方式处理
         NSURLSessionAuthChallengeUseCredential:使用指定的证书    NSURLSessionAuthChallengeCancelAuthenticationChallenge:取消请求
         */
        
        if (credential) {
            disposition = NSURLSessionAuthChallengeUseCredential;
        } else {
            disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        }
    } else {
        disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
    }
    
    //安装证书
    if (completionHandler) {
        completionHandler(disposition, credential);
    }
}

// 接收到服务器的响应
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
    NSLog(@"+++++++++++didReceiveResponse");
    completionHandler(NSURLSessionResponseAllow);
}

// 接收到服务器返回的数据
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
    
    //NSString * str =  [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
    //NSData *receiveData = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
    //NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

    NSLog(@"----------->>>>>didReceiveData--%lu-", (unsigned long)data.length);
    
}

// 请求完毕
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    NSLog(@"++++++++++++++didCompleteWithError=---%@", error);
}


打印结果如下:


2018-09-27 16:35:40.062008+0800 MOBFrameWorkTest[51074:1969808] ++++++++++didReceiveChallenge
2018-09-27 16:35:40.062133+0800 MOBFrameWorkTest[51074:1969808] ____authenticationMethod______NSURLAuthenticationMethodServerTrust
2018-09-27 16:35:40.062248+0800 MOBFrameWorkTest[51074:1969808] ------->>>>>>是服务器信任的证书
2018-09-27 16:35:40.382606+0800 MOBFrameWorkTest[51074:1969808] +++++++++++didReceiveResponse
2018-09-27 16:35:40.382938+0800 MOBFrameWorkTest[51074:1969808] ----------->>>>>didReceiveData--62096-
2018-09-27 16:35:40.383392+0800 MOBFrameWorkTest[51074:1969808] ++++++++++++++didCompleteWithError=---(null)


NSURLProtectionSpace

NSURLProtectionSpace对象表示需要身份验证的服务器或服务器的一部分。 保护空间定义了一系列匹配约束,用于确定应提供哪个凭证。

这个类没有指定的初始化程序; 它的init方法总是返回nil。 你必须通过调用Creating a protection space描述的初始化方法之一来初始化此类


//Creating a protection space
- (instancetype)initWithHost:(NSString *)host port:(NSInteger)port protocol:(NSString *)protocol realm:(NSString *)realm authenticationMethod:(NSString *)authenticationMethod

初始化一个保护空间对象,参数说明如下:


image.png

- (instancetype)initWithProxyHost:(NSString *)host port:(NSInteger)port type:(NSString *)type realm:(NSString *)realm authenticationMethod:(NSString *)authenticationMethod

初始化代理服务器的保护空间对象。

type:代理服务器的类型。 proxyType的值应设置为NSURLProtectionSpace Proxy Types(见后面)中指定的值之一。


//Getting protection space properties
@property(readonly, copy) NSString *authenticationMethod

调用者的认证方法;


@property(readonly, copy) NSArray<NSData *> *distinguishedNames

可接受的进行客户端证书认证的证书颁发机构。
如果保护空间的身份验证方法不是客户端证书,则为nil。 返回的颁发机构使用DER编码规则(DER)进行编码。


@property(readonly, copy) NSString *host

保护空间的主机名;


@property(readonly) BOOL isProxy

保护空间是否代表一个代理服务器;


@property(readonly) NSInteger port

保护空间的端口;


@property(readonly, copy) NSString *protocol

保护空间的协议;如果是代理服务器的保护空间,那么为nil;


@property(readonly, copy) NSString *proxyType

保护空间的代理类型;如果保护空间不是表示代理服务器,则为nil;


@property(readonly, copy) NSString *realm

保护空间的认证领域;
如果没有设置域,则为nil。 通常只有HTTP和HTTPS身份验证时才使用此属性。


@property(readonly) BOOL receivesCredentialSecurely

一个布尔值,指示是否可以安全地发送保护空间的凭据。


@property(readonly) SecTrustRef serverTrust

表示服务器的SSL事务状态。
如果保护空间的身份验证方法不是服务器信任的,则为nil。

NSURLProtectionSpace Protocol Types

这些常量描述了保护空间支持的协议;你可以在protocol属性找到这些值;

  • NSString *const NSURLProtectionSpaceHTT:HTTP
  • NSString *const NSURLProtectionSpaceHTTPS:HTTPS
  • NSString *const NSURLProtectionSpaceFTP: FTP
NSURLProtectionSpace Proxy Types

这些常量描述了代理的类型;你可以在proxyType属性找到这些值;

  • NSString *const NSURLProtectionSpaceHTTPProxy:HTTP代理
  • NSString *const NSURLProtectionSpaceHTTPSProxy:HTTPS代理
  • NSString *const NSURLProtectionSpaceFTPProxy:FTP代理
  • NSString *const NSURLProtectionSpaceSOCKSProxy:SOCKS代理
NSURLProtectionSpace Authentication Methods

这些常量描述了在初始化方法中可以用的认证方法,阐述如下:

NSURLAuthenticationMethodDefault : 对协议使用默认身份验证方法
NSURLAuthenticationMethodHTTPBasic:对保护空间使用HTTP基本身份验证,如果是HTTP请求,则此方法等同于NSURLAuthenticationMethodDefault
NSURLAuthenticationMethodHTTPDigest : 对保护空间使用HTTP摘要身份验证
NSURLAuthenticationMethodHTMLForm : 对保护空间使用HTML表单身份验证
NSURLAuthenticationMethodNegotiate : 对保护空间使用Kerberos或NTLM身份验证
NSURLAuthenticationMethodNTLM : 对保护空间使用NTLM身份验证
NSURLAuthenticationMethodClientCertificate : 对保护空间使用客户端证书验证,该认证方法可以应用于任何协议。
NSURLAuthenticationMethodServerTrust : 对保护空间执行服务器信任身份验证(证书验证)。此验证方法可以应用于任何协议,最常用于覆盖SSL和TLS链验证。
NSURLAuthenticationMethodHTMLForm:URL加载系统从不会根据此认证方法发出认证挑战

参考链接
保护网络传输的实现
https://www.jianshu.com/p/88336eab2b8d

相关文章

网友评论

      本文标题:iOS NSURLAuthenticationChallenge

      本文链接:https://www.haomeiwen.com/subject/vhaeoftx.html