1.设置代理
NSURLSession *sesson = [NSURLSession sessionWithConfiguration:
[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc]init]];
2.在代理方法中实现对证书的操作
方法一:这是在开发者足够信任后端的安全的情况下做的,比如调个接口,这样做的结果就是忽略证书的验证,直接信任。
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler{
if([challenge.protectionSpace.authenticationMethodisEqualToString:NSURLAuthenticationMethodServerTrust]){//服务器信任证书
NSURLCredential *credential = [NSURLCredentialcredentialForTrust:challenge.protectionSpace.serverTrust];//服务器信任证书
if(completionHandler)
completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
}
}
方法二:可以把证书加到工程中,然后https访问时在代理方法中进行证书的验证
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:
(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler{
SecTrustRef servertrust = challenge.protectionSpace.serverTrust;
SecCertificateRef certi= SecTrustGetCertificateAtIndex(servertrust, 0);
NSData *certidata = CFBridgingRelease(CFBridgingRetain(CFBridgingRelease(SecCertificateCopyData(certi))));
NSString *path = [[NSBundle mainBundle] pathForResource:@"https" ofType:@"cer"];
NSData *localCertiData = [NSData dataWithContentsOfFile:path];
if ([certidata isEqualToData:localCertiData]) {
NSURLCredential *credential = [[NSURLCredential alloc] initWithTrust:servertrust];
[challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
NSLog(@"服务端证书认证通过");
}else {
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
NSLog(@"服务端认证失败");
}
}
-(void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
NSURLCredential * credential = nil;
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if (self.sslChallengeMode == XKHTTPClientSSLChallengeModeNone) {
// 使用服务端的证书对服务端进行校验,那肯定是怎样都通过,自签名证书都能通过
disposition = NSURLSessionAuthChallengeUseCredential;
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
} else if(self.sslChallengeMode == XKHTTPClientSSLChallengeModeSimple) {
SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
NSArray * policies = @[ (__bridge_transfer id)SecPolicyCreateBasicX509() ];
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
SecTrustResultType result;
if(SecTrustEvaluate(serverTrust, &result) == errSecSuccess &&
(result == kSecTrustResultUnspecified || result == kSecTrustResultProceed)) {
// x509认证,不会对服务端证书中的域名和请求的域名进行一致性校验
// 校验成功,则直接传递服务端证书来对服务端鉴权进行校验,即:怎样都会成功
disposition = NSURLSessionAuthChallengeUseCredential;
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
} else {
// x509校验失败,则走默认的校验逻辑
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
// 默认校验逻辑:ATS,即对证书锚点、证书链进行校验且会对证书中的域名和请求的域名的一致性进行校验
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}
在了解了网络请求的流程之后,我们可以先测试一下自己项目网络请求耗时分布。
通过监听NSURLSession的didFinishCollectingMetrics回调方法,了解各种耗时操作
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0))
{
if (@available(iOS 10.0, *)) {
for (NSURLSessionTaskTransactionMetrics *sessionMetric in metrics.transactionMetrics) {
NSInteger dom = ([sessionMetric.domainLookupEndDate timeIntervalSince1970] - [sessionMetric.domainLookupStartDate timeIntervalSince1970]) * 1000 ;
NSInteger sec = ([sessionMetric.secureConnectionEndDate timeIntervalSince1970] - [sessionMetric.secureConnectionStartDate timeIntervalSince1970]) * 1000;
NSInteger con = ([sessionMetric.connectEndDate timeIntervalSince1970] - [sessionMetric.connectStartDate timeIntervalSince1970]) * 1000;
NSInteger req = ([sessionMetric.requestEndDate timeIntervalSince1970] - [sessionMetric.requestStartDate timeIntervalSince1970]) * 1000;
NSInteger res = ([sessionMetric.responseEndDate timeIntervalSince1970] - [sessionMetric.responseStartDate timeIntervalSince1970]) * 1000;
NSInteger tot = ([sessionMetric.responseEndDate timeIntervalSince1970] - [sessionMetric.fetchStartDate timeIntervalSince1970]) * 1000;
NSString *locip = @"";
NSString *remip = @"";
if (@available(iOS 13.0, *)) {
locip = [NSString stringWithFormat:@"%@", sessionMetric.localAddress];
remip = [NSString stringWithFormat:@"%@", sessionMetric.remoteAddress];
}
NSLog(@"metric path:%@ 总耗时:%ldms, 域名解析:%ldms, 连接耗时:%ldms(包括TLS:%ldms), 请求:%ldms, 回调:%ldms l:%@ r:%@",sessionMetric.request.URL.lastPathComponent,tot,dom,con,sec,req,res, locip, remip);
}
}
}
此时就可以进行对应的网络情况优化。