版本记录
版本号 | 时间 |
---|---|
V1.0 | 2018.03.12 |
前言
我们做APP发起网络请求,一般都是使用框架,这些框架的底层也都是苹果的API,接下来几篇就一起来看一下和网络有关的几个类。感兴趣的可以看上面几篇文章。
1. 详细解析几个和网络请求有关的类 (一) —— NSURLSession
2. 详细解析几个和网络请求有关的类(二) —— NSURLRequest和NSMutableURLRequest
3. 详细解析几个和网络请求有关的类(三) —— NSURLConnection
4. 详细解析几个和网络请求有关的类(四) —— NSURLSession和NSURLConnection的区别
5. 详细解析几个和网络请求有关的类(五) —— 关于NSURL加载系统(一)
6. 详细解析几个和网络请求有关的类(六) —— 使用NSURLSession(二)
7. 详细解析几个和网络请求有关的类(七) —— URL数据的编码和解码(三)
8. 详细解析几个和网络请求有关的类(八) —— 处理重定向和其他请求更改(四)
回顾
上一篇主要介绍处理重定向和其他请求更改,这一篇主要讲述身份验证挑战和TLS链验证。
身份验证挑战和TLS链验证
NSURLRequest
对象经常遇到身份验证质询,或者来自连接到的服务器的凭证请求。 NSURLSession
类在请求遇到身份验证质询时通知其代理,以便它们可以相应地执行操作。
重要提示:除非服务器响应包含
WWW-Authenticate
标头,否则URL加载系统类不会调用其代理来处理请求质询。 其他身份验证类型(如代理身份验证和TLS信任验证)不需要此标头。
Deciding How to Respond to an Authentication Challenge - 决定如何响应验证挑战
如果会话任务要求身份验证,并且没有可用的有效凭据(作为请求的URL的一部分或共享的NSURLCredentialStorage),则会创建身份验证质询。它首先发送URLSession:task:didReceiveChallenge:completionHandler:给它的任务代理来处理认证挑战。如果任务代理没有对该消息做出响应,则任务向其会话代理发送URLSession:task:didReceiveChallenge:completionHandler:
来处理身份验证质询。
为了继续连接,代表有三个选项:
- 提供认证凭证。
- 尝试继续没有凭据。
- 取消认证挑战。
为帮助确定正确的操作过程,传递给该方法的NSURLAuthenticationChallenge实例包含有关什么触发了身份验证质询,针对质询进行了多少次尝试,以前尝试的凭据,需要凭据的NSURLProtectionSpace
以及发送者挑战等信息。
如果身份验证质询尝试先前进行身份验证并失败(例如,如果用户在服务器上更改了自己的密码),则可以通过对身份验证质询调用proposalCredential
来获取尝试的凭据。委托人然后可以使用这些凭证来填充它向用户呈现的对话框。
在身份验证质询中调用previousFailureCount
将返回之前身份验证尝试的总次数,包括来自不同身份验证协议的身份验证次数。代理可以将此信息提供给用户,以确定它先前提供的凭证是否失败,或者限制最大验证次数。
Responding to an Authentication Challenge - 应对认证挑战
以下是您可以响应代理方法:URLSession:didReceiveChallenge:completionHandler:或URLSession:task:didReceiveChallenge:completionHandler:的三种方式。
1. Providing Credentials - 提供凭证
要尝试进行身份验证,应用程序应该创建一个NSURLCredential
对象,其中包含服务器期望的表单的身份验证信息。您可以通过在所提供的身份验证质询的保护空间中调用authenticationMethod来确定服务器的身份验证方法。 NSURLCredential
支持的一些认证方法是:
- HTTP基本认证
(NSURLAuthenticationMethodHTTPBasic)
需要用户名和密码。提示用户输入必要信息,并使用credentialWithUser:password:persistence:创建一个NSURLCredential
对象。 - HTTP摘要认证
(NSURLAuthenticationMethodHTTPDigest)
与基本认证一样,需要用户名和密码。 (摘要自动生成。)提示用户输入必要信息,并使用credentialWithUser:password:persistence:
创建一个NSURLCredential
对象。 - 客户端证书身份验证
(NSURLAuthenticationMethodClientCertificate)
需要系统身份和需要向服务器进行身份验证的所有证书。使用credentialWithIdentity:certificates:persistence:创建NSURLCredential
对象。 - 服务器信任认证(
NSURLAuthenticationMethodServerTrust)
需要由认证挑战的保护空间提供的信任。使用credentialForTrust:创建一个NSURLCredential
对象。
创建NSURLCredential
对象后,使用提供的完成处理程序块将对象传递给身份验证质询发送方。
2. Continuing Without Credentials - 继续没有证书
如果代理选择不提供身份验证质询的凭证,则可以尝试在没有身份验证质询的情况下继续。 将以下值之一传递给提供的完成处理程序块:
- NSURLSessionAuthChallengePerformDefaultHandling处理请求,就好像代理没有提供代理方法来处理挑战一样。
- NSURLSessionAuthChallengeRejectProtectionSpace拒绝挑战。 根据服务器响应允许的身份验证类型,URL加载类可能会多次调用此代理方法,以获得额外的保护空间。
3. Canceling the Connection - 取消连接
代理也可以选择通过将NSURLSessionAuthChallengeCancelAuthenticationChallenge传递给提供的完成处理程序块来取消认证挑战。
4. An Authentication Example - 一个认证示例
Listing 4-1所示的实现通过创建一个NSURLCredential
实例来回应挑战,该实例具有应用程序首选项提供的用户名和密码。 如果之前认证失败,则取消认证质询并通知用户。
// Listing 4-1 An example of using the URLSession:didReceiveChallenge:completionHandler: delegate method
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler
{
if ([challenge previousFailureCount] == 0) {
NSURLCredential *newCredential = [NSURLCredential credentialWithUser:[self preferencesName]
password:[self preferencesPassword]
persistence:NSURLCredentialPersistenceNone];
completionHandler(NSURLSessionAuthChallengeUseCredential, newCredential);
} else {
// Inform the user that the user name and password are incorrect
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
}
}
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
guard challenge.previousFailureCount == 0 else {
challenge.sender?.cancel(challenge)
// Inform the user that the user name and password are incorrect
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
let proposedCredential = URLCredential(user: self.preferencesName, password: self.preferencesPassword, persistence: .none)
completionHandler(.useCredential, proposedCredential)
}
如果认证挑战未被会话或任务代理处理,并且凭证不可用或者未通过身份验证,则基础实现将发送continueWithoutCredentialForAuthenticationChallenge:
消息。
Performing Custom TLS Chain Validation - 执行自定义TLS链验证
在NSURL系列API中,TLS链验证由应用程序的身份验证代理方法处理,但您的应用程序不是提供验证用户(或您的应用程序)到服务器的凭据,而是检查服务器在TLS握手期间提供的凭据,然后告诉URL加载系统它应该接受还是拒绝这些凭证。
如果您需要以非标准方式执行链验证(例如接受特定的自签名证书进行测试),则您的应用必须实现URLSession:didReceiveChallenge:completionHandler:
或URLSession:task:didReceiveChallenge:completionHandler:
代理方法。如果您同时实施,则会话级别的方法负责处理身份验证。
在您的身份验证处理程序代理方法中,您应该检查以确定质询保护空间是否具有NSURLAuthenticationMethodServerTrust
的身份验证类型,如果是,则从该保护空间获取serverTrust
信息
后记
本篇要讲述身份验证挑战和TLS链验证,希望大家继续关注我。