写在前面
最近重读了AFNetworking 4.x的源码,算是温故而知新吧.也梳理了一些优秀的代码细节和面试考点,罗列下来,发现这个库小而精致,简直初学者的宝藏库.
一、开源库怎么看?
先说个题外话,阅读优质的开源代码库,绝对是程序员们快速提升自我的有效途径,而怎样高效率的去阅读源码同样也是一个问题,不知道有没有人和我之前一样,碰到过读倒是读了,但总感觉收获不大的情况.
这里分享一下我的一些读码经验:
-
多思考,多抛出问题,比如说
- 整体的代码结构是怎样的?类与类之间的关系是怎样的?为什么要这么设计?
- 代码有没有涉及到多线程,其线程模型是怎样的?哪类问题可以适用这种多线程的方案?
- 代码中使用了哪些设计模式?具体是怎么实现的?
-
也可以关注代码细节,遇到不熟悉的用法不要放过,多刨根究底才能夯实基础
关于
AFNetworking
的一些优秀代码细节,我这里也整理了一部分,可以查阅后文 -
一定要记笔记和总结,能分享更好
参考费曼学习法,我认为这一点是最好的加深理解和强化记忆的手段.随着年龄的增大,记忆力会有所衰退,有个笔记能够回顾,能节约大把再次记忆的时间.此外,多与人分享,与人交流验证,也能够为自己查缺补漏.
二、AFNetworking 4.x的代码结构
还是说回到AFNetworking
这里,AF
的代码结构大部分人应该都了解,这里我先简单介绍下.AFNetworking 4.x
的代码结构比2.x
要简单许多,主要也得益于苹果优化了网络相关的api
,整体代码有这么几部分:
从
AF3.x
版本开始剔除了NSURLConnection
-
AFURLSessionManager/AFHTTPSessionManager
这里就是
AF
代码的核心了,主要负责网络请求的发起,回调处理,是在系统网络相关API
上的一层封装.大部分逻辑是在AFURLSessionManager
里面处理的,AFHTTPSessionManager
则是专为HTTP
请求提供了一些便利方法.如果需要扩展其他协议的功能(比如FTP
协议),可以考虑从AFURLSessionManager
创建一个子类. -
AFURLRequestSerialization/AFURLResponseSerialization
这两兄弟主要处理一些参数序列化相关的工作.
AFURLRequestSerialization
是将传入的参数构造成NSURLRequest
,比如自定义的header
,一些post
或者get
参数等等.AFURLResponseSerialization
主要是将系统返回的NSURLResponse
处理成我们需要的responseObject
,比如json、xml、image
等等. -
AFSecurityPolicy
处理
https
相关的公钥和验证逻辑.目前由于苹果ATS
的开启,基本HTTPS
已经成为标配.虽然通常直接使用CA
来验证服务器公钥的情况下,不需要我们额外做什么配置.但是从这里出发,顺便考察一下HTTPS
相关的知识点,感觉也比较常见,具体面试题可看下文 -
AFNetworkReachabilityManager
这个其实是比较独立的一个模块了,提供获取当前网络状态的功能.
-
UIKit+AFNetworking
这里主要是通过
Category
来提供了一下UIkit
的便利方法
AFURLSessionManager/AFHTTPSessionManager
① manager初始化
说到manager
的初始化.我想问问大家manager
的设计模式是什么呢?
我猜想肯定有一大部分人会说是单例模式
,哈哈,如果你在面试的时候这样说,那面试官可能会叫你回去等消息吧.
其实manager
所用到的设计模式是工厂设计模式
.
查看父类
AFURLSessionManager
的initWithSessionConfiguration
方法
这其中,又有一个考点哦.即
self.operationQueue.maxConcurrentOperationCount = 1;
为什么要这样呢?能不能多开几个线程呢?---答案是不能
,鉴于一些多线程数据访问的安全性考虑.同时苹果官方也告诉我们不能
② request方法封装
我们都知道一个完整的请求 request
应该包括 请求行+请求头+请求体
.
请求行
我们后面再讲,先讲讲AF
是如何封装请求头
和请求体
的.
以get
请求为例来看看,进入到AF
中get
请求实现的方法
这里主要是返回一个
dataTask
和开始网络请求
,并没有告诉我们是如何封装请求头的,我们继续进入dataTaskWithHTTPMethod:
方法
这个方法作用主要是:
- ①
生成request
- ② 通过
request
生成dataTask
.
那么它是如何封装生成request的呢?快跟上我的车队,继续进入requestWithMethod:
方法
在
requestWithMethod
方法中,做了三件事情:
- ① 创建
mutableRequest
并设置其请求方法; - ② 把当前类设置的一些属性设置给
mutableRequest
--请求头的配置
;
如果我们没有设置超时时间,那就是使用默认的超时时间为60s...
这里主要利用KVO
的响应式编程技术
- ③ 把需要传递的参数进行编码并且设置到
mutableRequest
当中 --请求参数封装
,调用requestBySerializingRequest:
方法这个方法中主要做了以下几件事:
- ① 从当前请求队列中拿到
self.HTTPRequestHeaders
的参数,赋值到要请求的request
中. - ② 把
网络请求的参数
转换为NSString
类型,这一步是对参数进行转码
-- 这是我们的重点,主要是调用AFQueryStringFromParameters
方法. - ③ 将请求方法拼接到
url
中:GET、HEAD、DELETE
这几个method
的query
是拼接到url
后面的,而POST、PUT
是把query
拼接到http body
中的.
接下来我们着重来看看,AF
是怎么把我们传入的参数字典转成字符串的.进入AFQueryStringFromParameters
方法
通过递归调用并解析,直到解析的除了
array,dic,set
以外的元素,然后将最终得到的参数返回,接着③的步骤继续处理.
③ task与代理的关系
接着上面的分析,我们刚刚讲了AF
如何把网络请求的参数
转换为NSString
类型,现在我们来看看AF
又是如何把请求方法拼接到url
中的---这就来到了我们的重点task
与代理
的关系.进入dataTaskWithRequest:
方法
这里要注意一点这个方法是由
AFURLSessionManager
管理的,而不是AFURLSessionManagerTaskDelegate
那么最后他们为什么要关联以及关联在一起呢?想知道吗?想的话我们继续
结合前面我们有这时的持有关系为
manager-->session-->task--->delegate--->manager
,那这不就构成了循环引用了吗?那AF是怎么解决这个问题的呢??
之后又回到我们的
经过上面的过程,接下来就是调用[dataTask resume];
方法了.这个resume
是有问题的,老铁,AF
在底层有给它做了一些骚操作..这里我将揭露他的秘密之处.
其实他在这里做了一个内部类_AFURLSessionTaskSwizzling
,在这个类里面将resume
和af_resume
进行了交换.
我们在来看看
AF
搞的骚操作的af_resume
的实现
其实在这个
AF
框架里面,我们都能拿到task
状态.那么这个通知是给谁的呢?来一起来瞧瞧
竟然给了
AFURLSessionManager
.因为它一个大管家,权限大.哈哈.
最后来总结一下这部分的整个流程,如下所示
AFURLRequestSerialization/AFURLResponseSerialization
① NSObject, NSSecureCoding, NSCopying协议
来到AFURLRequestSerialization
类声明的地址,我们会发现它遵循了NSObject, NSSecureCoding, NSCopying
这个三个协议.
主要是让
AFURLRequestSerialization
具备copy
和归档
等一系列功能.
② 多表单对数据的封装
对于多表单是怎么处理的呢?来直接看源码
通过上面的方法(在上传图片或断点续传的时候调用)进入
multipartFormRequestWithMethod:
方法
我们先来看下生产
formData
的initWithURLRequest:
方法
接着在来看看
AFCreateMultipartFormBoundary
这个分隔符
接着我们返回来,继续看
AF
是如何处理parameters
的,它将进行一系列的格式化,生成相应的AFQueryStringPair
对,最后进行拼接调用appendPartWithFormData
方法
其整体流程就是
这里要注意多表单
的Content-Type
和普通post
表单的是不同的,普通的Content-Type
是
而
多表单
的则是
接着来看看
contentLength
是怎么拼接的
② Stream流程
那么拼接后的数据又是如何处理的呢?回顾前面说的
这个方法中就设置了
stream
.先来看看HTTPBodyStream
属性
源码告诉我们
stream
默认是关闭的,我们要先开启,在去读它.之后在调用NSInputStream
相关的代理.
且
HTTPBodyStream
和HTTPBody
是相冲的,只能设置一个.
在调用resume
方法之前,会先进行下面的方法.
这方法里面主要是调用
bodypart
的接口方法:read:...maxLength
接着进入
transitionToNextPhase
,在这个方法里面就开启了我们的stream
那么
transitionToNextPhase
这个方法又是什么时候调用的呢?其实它在init
初始化的时候就调用了
③ AFURLResponse
那么什么时候会来AFURLResponse
呢? manager -- task --请求完成
的时候,后台有了返回的时候.回到NSURLSessionTaskDelegate
代理方法中的if(error)else
部分
这其中包含了我们开发中的各种序列化
来看我们常用的
[AFJSONResponseSerializer responseObjectForResponse:data:error:]
方法
- ①先看验证请求判断这块,他是为了验证什么呢?
- ② 如果验证通过之后就进行
JSON
数据序列化,这其中有一点需要说一下就是移除返回为
null的数据
.
这部分内容的流程大概如下所示
AFSecurityPolicy
① http简介
② https
HTTPS和HTTP的区别
HTTPS协议 = HTTP协议 + SSL/TLS协议 SSL的全称是Secure Sockets Layer,即安全套接层协议,是为网络通信提供安全及数据完整性的一种安全协议.TLS的全称是Transport Layer Security,即安全传输层协议. 即HTTPS是安全的HTTP.
HTTPS的连接建立流程
HTTPS为了兼顾安全与效率,同时使用了对称加密和非对称加密。在传输的过程中会涉及到三个密钥:
-
服务器端的公钥和私钥,用来进行
非对称加密
-
客户端生成的随机密钥,用来进行
对称加密
如上图,HTTPS连接过程大致可分为八步: 1、客户端访问HTTPS连接
客户端会把安全协议版本号
、客户端支持的加密算法列表、随机数C
发给服务端。
2、服务端发送证书给客户端
服务端接收密钥算法配件后,会和自己支持的加密算法列表进行比对,如果不符合,则断开连接。否则,服务端会在该算法列表中,选择一种对称算法(如AES)、一种公钥算法(如具有特定秘钥长度的RSA)和一种MAC算法发给客户端.
服务器端有一个密钥对,即公钥
和私钥
,是用来进行非对称加密
使用的,服务器端保存着私钥
,不能将其泄露,公钥
可以发送给任何人。
在发送加密算法的同时还会把数字证书
和随机数S
发送给客户端
3、客户端验证server证书
会对server公钥进行检查,验证其合法性,如果发现公钥有问题,那么HTTPS传输就无法继续。
4、客户端组装会话秘钥
如果公钥合格,那么客户端会用服务器公钥来生成一个前主秘钥
(Pre-Master Secret,PMS),并通过该前主秘钥
和随机数C、S来组装成会话秘钥
5、客户端将前主秘钥加密发送给服务端
是通过服务端的公钥来对前主秘钥进行非对称加密,发送给服务端
6、服务端通过私钥解密得到前主秘钥
服务端接收到加密信息后,用私钥解密得到主秘钥.
7、服务端组装会话秘钥
服务端通过前主秘钥
和随机数C、S来组装会话秘钥
.
至此,服务端和客户端都已经知道了用于此次会话的主秘钥.
8、数据传输
客户端收到服务器发送来的密文,用客户端密钥对其进行对称解密,得到服务器发送的数据。 同理,服务端收到客户端发送来的密文,用服务端密钥对其进行对称解密,得到客户端发送的数据.
总结:
会话秘钥
= random S + random C + 前主秘钥
-
HTTPS连接建立过程使用
非对称加密
,而非对称加密
是很耗时的一种加密方式 -
后续通信过程使用
对称加密
,减少耗时所带来的性能损耗 -
其中,
对称加密
加密的是实际的数据,非对称加密
加密的是对称加密所需要的客户端的密钥.
对称加密和非对称加密
1、对称加密
用同一套密钥来进行加密解密. 对称加密通常有 DES,IDEA,3DES 加密算法.
2、非对称加密
用公钥和私钥来加解密的算法。
公钥
(Public Key)与私钥
(Private Key)是通过一种算法得到的一个密钥对(即一个公钥
和一个私钥
),公钥
是密钥对中公开的部分,私钥
则是非公开的部分,私钥
通常是保存在本地。
-
用
公钥
进行加密,就要用私钥
进行解密;反之,用私钥
加密,就要用公钥
进行解密(数字签名). -
由于私钥是保存在本地的,所以
非对称加密
相对与对称加密
是安全的. 但非对称加密
比对称加密
耗时(100倍以上),所以通常要结合对称加密
来使用.
常见的非对称加密算法有:RSA、ECC(移动设备用)、Diffie-Hellman、El Gamal、DSA(数字签名用)
而为了确保客户端能够确认公钥就是想要访问的网站的公钥,引入了数字证书的概念,由于证书存在一级一级的签发过程,所以就出现了证书链,在证书链中的顶端的就是根CA.
③ AFSecurityPolicy
来看看
AF
的源码
那么它是如何取出公钥的呢?继续跟进setPinnedCertificates
方法
在取公钥的过程可能有两种验证方式,单向验证(服务端)和双向验证(客服端和服务端):
在取出公钥传输的过程中会验证所有证书的信息.
在后面这个代理方法里面会进入
evaluateServerTrust:forDomain:
方法,用来验证服务端是否值得信任.
假如你的证书是不受信任的话(即自签证书),那他会进入
也就是说如果你的证书是
CA
的就不需要处理,是自签的话就需要设置为根证书.
AFNetworkReachabilityManager
先上个流程图
话不多说来上源码,这个类主要用要到了全局单例的设计模式
接着调用
manager
方法
跟进
managerForAddress
方法
返回ReachabilityManager
对象后调用startMonitoring
方法
接着根据网络
flag
转成我们开发中常用的网络状态调用AFNetworkReachabilityStatus()
UIKit+AFNetworking
AFAutoPurgingImageCache
该类是用来管理内存中的缓存图片的,它提供了最大内存容量和首选内存容量,当达到最大容量时,会依次删除(Purging
)最久未使用的缓存图片,直到降到首选内存容量以下.让我们看一下这个功能是如何实现的?
AFImageCache协议
这个协议定义了针对缓存图片的增删改查
的方法,这些方法都是同步并安全的:
@protocol AFImageCache <NSObject>
- (void)addImage:(UIImage *)image withIdentifier:(NSString *)identifier;
- (BOOL)removeImageWithIdentifier:(NSString *)identifier;
- (BOOL)removeAllImages;
- (nullable UIImage *)imageWithIdentifier:(NSString *)identifier;
@end
这些方法中涉及到入参identifier
,这个值可以作为图片id
用来查找图片,一般可以用图片名来表示,当然网络图片也可以用url
来表示,针对这一点,AF
对这个协议进行了扩展.
AFImageRequestCache
@protocol AFImageRequestCache <AFImageCache>
// 是否应该缓存,默认实现是YES
- (BOOL)shouldCacheImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier;
// 增删查方法,支持了传入一个request对象,并以request.URL.absoluteString为图片identifier,同时也可以在默认identifier后拼接AdditionalIdentifier
- (void)addImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier;
- (BOOL)removeImageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier;
- (nullable UIImage *)imageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier;
@end
看完协议的定义后,我们继续看一下这个协议的主要实现AFAutoPurgingImageCache
类
AFAutoPurgingImageCache
这个类中除了实现AFImageCache
协议相关方法,还增加了内存控制的属性:
// 最大内存
@property (nonatomic, assign) UInt64 memoryCapacity;
// 首选内存
@property (nonatomic, assign) UInt64 preferredMemoryUsageAfterPurge;
// 当前内存用量
@property (nonatomic, assign, readonly) UInt64 memoryUsage;
// init
- (instancetype)initWithMemoryCapacity:(UInt64)memoryCapacity preferredMemoryCapacity:(UInt64)preferredMemoryCapacity;
属性都很好理解,这些属性是如何使用的要看具体的实现了:
@interface AFAutoPurgingImageCache ()
// 用来管理缓存图片的字典
@property (nonatomic, strong) NSMutableDictionary <NSString* , AFCachedImage*> *cachedImages;
// 当前使用的内存
@property (nonatomic, assign) UInt64 currentMemoryUsage;
// 保证安全性的队列
@property (nonatomic, strong) dispatch_queue_t synchronizationQueue;
@end
可以看到这个类的内部有一个用来管理缓存图片的字典
,这个字典的key
为图片的identifier
,value
为AFCachedImage
对象,对于这个对象,我们看下它的实现:
@interface AFCachedImage : NSObject
@property (nonatomic, strong) UIImage *image;//持有image
@property (nonatomic, copy) NSString *identifier;//唯一标识
@property (nonatomic, assign) UInt64 totalBytes;//图片占用的总字节数
@property (nonatomic, strong) NSDate *lastAccessDate;//上次获取的时间
@property (nonatomic, assign) UInt64 currentMemoryUsage;//当前使用的内存
@end
@implementation AFCachedImage
- (instancetype)initWithImage:(UIImage *)image identifier:(NSString *)identifier {
if (self = [self init]) {
self.image = image;
self.identifier = identifier;
// 计算当前的图片总字节数
CGSize imageSize = CGSizeMake(image.size.width * image.scale, image.size.height * image.scale);
CGFloat bytesPerPixel = 4.0;
CGFloat bytesPerSize = imageSize.width * imageSize.height;
self.totalBytes = (UInt64)bytesPerPixel * (UInt64)bytesPerSize;
self.lastAccessDate = [NSDate date];
}
return self;
}
- (UIImage *)accessImage {
// 每次获取图片都会刷新时间
self.lastAccessDate = [NSDate date];
return self.image;
}
@end
可以看到AFCachedImage
类相当于对UIImage
进行了包装,添加了一些标识类的属性而已,现在我们回到AFAutoPurgingImageCache
中继续往下看它的方法实现:
以上就是
AF
的图片缓存,大致的流程为
AFImageDownloader
下载图片类,这个类会并行下载图片任务,下载后的图片会缓存到内存中.
说到这我们用的最多的就是给UIimageView
设置图片了,这其中又需要用到AF
的UIImageView+AFNetworking
分类了.
例如[cell.imageView setImageWithURL:[NSURL URLWithString:model.imageUrl]];
这时候会来到AF
的UIImageView+AFNetworking
分类中的setImageWithURL:
方法
为什么
AFImageDownloader *downloader = [[self class] sharedImageDownloader];
中用sharedImageDownloader
呢?意味着我所有的下载用一个下载器下载就行了.这个下载器关联到AF
的分类里面,所以用关联属性进行设置.
接着会初始化一个下载凭证
AFImageDownloadReceipt *receipt;
,下载凭证会调用downloadImageForURLRequest:
方法
梳理下大致逻辑,当遇到需要加载网络图片的情况下时,我们调用
downloadImageForURLRequest
方法创建任务,并生成receipt
返回给我们,方便我们随时取消.加载过程会优先根据缓存策略,选择是否去缓存中查找,请求成功后会把当前图片放到内存中方便下次使用.
AF
下载图片的大致流程如下
三、AF的一些优质代码细节
仔细瞅瞅代码之后,发现常见的OC基础知识
在AF
里面都有具体应用,挺多还是面试题考点,这里也是做个记录和梳理.
-
单例的创建方法
通过
dispatch_once
来保证多线程调用时,只有一个实例被创建. -
dispatch_sync与dispatch_barrier_sync配合解决并行读串行写问题
GCD
使用barrier
来处理并行读串行写
问题的具体用法 -
weakSelf与strongSelf的用法 -- 强弱共舞
必知必会,
weakSelf
避免循环引用,strongSelf
保证block
内部执行过程中self
不会被释放.
四、AFNetworking的可能面试考点
前面提到阅读开源库时,要多思考多提问题,这里也结合一些面试考题来梳理下
AFNetworking 2.x怎么开启常驻子线程?为何需要常驻子线程?
这个知识点应该是AF2.x
版本面试官比较喜欢问的了,AF2.x
版本有个细节,通过runloop
开启了一个常驻子线程,具体代码是这样的:
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
@autoreleasepool {
[[NSThread currentThread] setName:@"AFNetworking"];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
}
}
+ (NSThread *)networkRequestThread {
static NSThread *_networkRequestThread = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
[_networkRequestThread start];
});
return _networkRequestThread;
}
首先,我们要了解为何要开启常驻子线程?
NSURLConnection
的接口是异步
的,然后会再发起线程的回调.而一个子线程,在同步代码执行完成之后,一般情况下,线程就退出了.那么想要接收到NSURLConnection
的回调,就必须让子线程至少存活到回调的时机.而AF
让线程常驻的原因是,当发起多个http请求的时候,会统一在这个子线程进行回调的处理
,所以干脆就让其一直存活下来.
上面说的一般情况,子线程执行完任务就会退出
,那么什么情况下,子线程能够继续存活呢?这就涉及到第二个问题了,AF
是如何开启常驻线程的,这里实际上考察的是runloop
的基础知识.
关于runloop
,可以看下我的iOS之武功秘籍⑲: 内存管理与NSRunLoop
.这里简单来说,当runloop
发现还有source/timer/observer
的时候,runloop
就不会退出.所以AF
这里就通过给当前runloop
添加一个NSMachPort
,这个port
实际上相当于添加了一个source
事件源,这样子线程的runloop
就会一直处于循环状态,等待别的线程向这个port
发送消息,而实际上AF
这里是没有消息发送到这个port
的.
除了AF
的这种处理方式,实际上苹果也提供了回调线程的解决方案:
// NSURLConnection
- (void)setDelegateQueue:(nullable NSOperationQueue*) queue
// NSURLSession
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(nullable id <NSURLSessionDelegate>)delegate delegateQueue:(nullable NSOperationQueue *)queue;
苹果提供了接口,可以让你制定一个operationQueue
供回调执行.所以从AF3.x
版本开始,就直接创建了一个并发度为1
的队列,来处理回调.
扩展一:
面试官可能会问你:为什么从AF3.x
开始需要设置maxConcurrentOperationCount = 1
,而AF2.x
却不需要?
这个问题不难,但是却可以帮助面试官判断面试者是否真的认真研读了AF
的两个大版本的源码.
解答:功能不一样:AF3.x
开始的operationQueue
是用来接收NSURLSessionDelegate
回调的,鉴于一些多线程数据访问的安全性考虑,设置了maxConcurrentOperationCount = 1
来达到串行回调的效果
.
而AF2.x
的operationQueue
是用来添加operation
并进行并发请求的,所以不要设置为1.
AFURLSessionManager与NSURLSession的关系,每次都需要新建mananger吗?
我们如果仔细查看代码,应该就能得出这样的结论:manager
与session
是1对1
的关系,AF
会在manager
初始化的时候创建对应的NSURLSession
.
那么复用manager
实际上就是复用了session
,而复用session
可以带来什么好处呢?
其实iOS9
之后,session
就开始支持http2.0
.而http2.0
的一个特点就是多路复用
。所以这里复用session
其实就是在利用http2.0
的多路复用特点,减少访问同一个服务器时,重新建立tcp
连接的耗时和资源.
官方文档也推荐在不同的功能场景下,使用不同的session
.比如:一个session
处理普通的请求,一个session
处理background
请求;1个session
处理浏览器公开的请求,一个session
专门处理隐私请求等等场景.
AFSecurityPolicy如何避免中间人攻击?
现在,由于苹果ATS
的策略,基本都切到HTTPS
了,HTTPS
的基本原理还是需要了解一下的,这里不做介绍,需要的可以google
查阅一下相关文章.
通常,首先我们要了解中间人攻击,大体就是黑客通过截获服务器返回的证书,并伪造成自己的证书,通常我们使用的Charles/Fiddler
等工具实际上就可以看成中间人攻击.
解决方案其实也很简单,就是SSL Pinning
.AFSecurityPolicy
的AFSSLPinningMode
就是相关设置项.
SSL Pinning
的原理就是需要将服务器的公钥打包到客户端中,tls
验证时,会将服务器的证书和本地的证书做一个对比,一致的话才允许验证通过.
由于数字证书存在有效期,内置到客户端后就存在失效后导致验证失败的问题,所以可以考虑设置为
AFSSLPinningModePublicKey
的模式,这样的话,只要保证证书续期后,证书中的公钥不变,就能够通过验证了.
写在后面
和谐学习,不急不躁.我还是我,颜色不一样的烟火.