AFSecurityPolicy
AFSecurityPolicy
用于公钥安全连接和对X.509证书的服务器信任验证,数字证书的格式遵循X.509标准,X.509是由国际电信联盟(ITU-T)制定的数字证书标准。
-
AFSSLPinningMode SSLPinningMode
根据固定的SSL证书对服务器信任进行评估的标准,默认为“AFSSLPinningModeNone”。 -
NSSet <NSData *> *pinnedCertificates
用于根据SSL连接模式评估服务器信任的证书。 -
BOOL allowInvalidCertificates
是否信任服务器无效或过期的SSL证书。默认为“NO”。 -
validatesDomainName
是否验证证书CN字段中的域名。默认为“YES”。
#pragma mark -根据severTrust和domain检测服务端发送的证书是否可信
// SecTrustRef是一个CoreFoundation类型,用于对服务器端传来的X.509证书评估
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
forDomain:(NSString *)domain
{
//如果domain存在并且允许使用自建证书;证书中的域名有效;SSLPinningMode模式为AFSSLPinningModeNone(不使用SSL pinning)或者本地没有证书文件的时候,则返回NO。也就是说需要验证自签名证书的话,就必须把对应证书导入到本地,并更改AFSSLPinningMode
if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {
// https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html
// According to the docs, you should only trust your provided certs for evaluation.
// Pinned certificates are added to the trust. Without pinned certificates,
// there is nothing to evaluate against.
//
// From Apple Docs:
// "Do not implicitly trust self-signed certificates as anchors (kSecTrustOptionImplicitAnchors).
// Instead, add your own (self-signed) CA certificate to the list of trusted anchors."
NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning.");
return NO;
}
NSMutableArray *policies = [NSMutableArray array];
if (self.validatesDomainName) {
// 如果需要验证domain,就使用SecPolicyCreateSSL函数创建验证策略,其中第一个参数为true表示验证整个SSL证书链,第二个参数传入domain,用于判断整个证书链上叶子节点表示的那个domain是否和此处传入domain一致
[policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
} else {
// 如果不需要验证domain,就使用默认的BasicX509验证策略
[policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
}
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
// 如果SSLPinningMode为 AFSSLPinningModeNone,表示不使用SSL pinning,是否允许自建证书或者AFServerTrustIsValid检测serverTrust是否可信任,如果信任,返回YES
if (self.SSLPinningMode == AFSSLPinningModeNone) {
return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
} else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
// 既不允许自建证书,并且AFServerTrustIsValid又返回NO,那么该serverTrust不能通过验证
return NO;
}
switch (self.SSLPinningMode) {
case AFSSLPinningModeNone:
default:
return NO;
case AFSSLPinningModeCertificate: {
//当使用SSL Pinning Mode时,也就是本地导入自检证书时,遍历pinnedCertificates集合中的证书文件,调用AFServerTrustIsValid方法中的SecTrustEvaluate进行验证,如果不能被信任,返回NO;
NSMutableArray *pinnedCertificates = [NSMutableArray array];
for (NSData *certificateData in self.pinnedCertificates) {
[pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
}
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
if (!AFServerTrustIsValid(serverTrust)) {
return NO;
}
//返回顺序是从叶子节点到根节点的可信任的证书链
NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
//从下遍历服务端返回的证书链的证书,如果和本地绑定的证书一致则返回YES
//从服务器端证书链的根节点往下遍历,因为遍历顺序正好相反,所以使用reverseObjectEnumerator
for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
return YES;
}
}
return NO;
}
case AFSSLPinningModePublicKey: {
// AFSSLPinningModePublicKey模式也是使是用证书绑定(SSL Pinning)方式验证,本地要有对应证书,但是验证时只验证证书里的公钥,不验证证书的有效期等信息。只要公钥是正确的就算通过
NSUInteger trustedPublicKeyCount = 0;
//从serverTrust中取出对应证书的公钥
NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);
// 依次遍历,如果和客户端绑定证书的公钥一致,就给trustedPublicKeyCount加1
for (id trustChainPublicKey in publicKeys) {
for (id pinnedPublicKey in self.pinnedPublicKeys) {
if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
trustedPublicKeyCount += 1;
}
}
}
return trustedPublicKeyCount > 0;
}
}
return NO;
}
AFHTTPResponseSerializer
AFHTTPResponseSerializer遵从AFURLRequestSerialization
和AFURLResponseSerialization
(数据响应序列化,如JSON响应序列化程序可以检查一个可接受的状态代码(2xx
范围)和内容类型(application/json
),将可用的JSON转化为一个对象)协议,提供一个具体的基本实现URL的查询字符串/表单编码的参数序列化和默认请求头,以及响应状态码和内容类型验证。
对应的他也衍生出一些子类用于解析不同的数据类型:
-
AFJSONResponseSerializer
-
AFXMLParserResponseSerializer
-
AFXMLDocumentResponseSerializer
-
AFPropertyListResponseSerializer
-
AFImageResponseSerializer
-
AFCompoundResponseSerializer
其实他们都是实现了- (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error
方法,在里面解析成对应需要的数据。
对于**AFHTTPResponseSerializer **:
#pragma mark -
- (BOOL)validateResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
error:(NSError * __autoreleasing *)error
{
BOOL responseIsValid = YES;
NSError *validationError = nil;
//如果不为空并且类型正确则解析
if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
//解析的acceptableContentTypes不包含响应的IMMEType
if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]] &&
!([response MIMEType] == nil && [data length] == 0)) {
if ([data length] > 0 && [response URL]) {
NSMutableDictionary *mutableUserInfo = [@{
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"AFNetworking", nil), [response MIMEType]],
NSURLErrorFailingURLErrorKey:[response URL],
AFNetworkingOperationFailingURLResponseErrorKey: response,
} mutableCopy];
if (data) {
mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
}
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError);
}
responseIsValid = NO;
}
if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
NSMutableDictionary *mutableUserInfo = [@{
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],
NSURLErrorFailingURLErrorKey:[response URL],
AFNetworkingOperationFailingURLResponseErrorKey: response,
} mutableCopy];
if (data) {
mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
}
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
responseIsValid = NO;
}
}
if (error && !responseIsValid) {
*error = validationError;
}
return responseIsValid;
}
AFJSONResponseSerializer
self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];
接收类型为"application/json","text/json","text/javascript"
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
//如果接收的ContentTypes不正确,或者statusCode不满足
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
return nil;
}
}
// Workaround for behavior of Rails to return a single space for `head :ok` (a workaround for a bug in Safari), which is not interpreted as valid input by NSJSONSerialization.
// See https://github.com/rails/rails/issues/1742
BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]];
//如果返回数据是单个空格则不解析
if (data.length == 0 || isSpace) {
return nil;
}
NSError *serializationError = nil;
id responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];
if (!responseObject)
{
if (error) {
*error = AFErrorWithUnderlyingError(serializationError, *error);
}
return nil;
}
//如果需要移除JSON中key对应的values为空的情况,调用AFJSONObjectByRemovingKeysWithNullValues
if (self.removesKeysWithNullValues) {
return AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
}
return responseObject;
}
AFXMLParserResponseSerializer
self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/xml", @"text/xml", nil];
对应的ContentType为application/xml
和text/xml
- (id)responseObjectForResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
return nil;
}
}
//解析方法是使用NSXMLParser
return [[NSXMLParser alloc] initWithData:data];
}
AFXMLDocumentResponseSerializer
同理,ContentType为application/xml
和text/xml
,不同的地方是使用在MAC_OS_X系统。
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
return nil;
}
}
//使用NSXMLDocument进行解析
NSError *serializationError = nil;
NSXMLDocument *document = [[NSXMLDocument alloc] initWithData:data options:self.options error:&serializationError];
if (!document)
{
if (error) {
*error = AFErrorWithUnderlyingError(serializationError, *error);
}
return nil;
}
return document;
}
AFPropertyListResponseSerializer
ContentType为"application/x-plist"
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
return nil;
}
}
if (!data) {
return nil;
}
NSError *serializationError = nil;
//使用NSPropertyListSerialization解析
id responseObject = [NSPropertyListSerialization propertyListWithData:data options:self.readOptions format:NULL error:&serializationError];
if (!responseObject)
{
if (error) {
*error = AFErrorWithUnderlyingError(serializationError, *error);
}
return nil;
}
return responseObject;
}
AFImageResponseSerializer
当ContentType为@"image/tiff", @"image/jpeg", @"image/gif", @"image/png", @"image/ico", @"image/x-icon", @"image/bmp", @"image/x-bmp", @"image/x-xbitmap", @"image/x-win-bitmap", MIMEType为"image/png"
"image/jpeg"
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
return nil;
}
}
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
//是否允许自动填充响应图像数据压缩格式(如PNG和JPEG),当使用`setCompletionBlockWithSuccess:failure:`方法时在iOS上可以显著提高性能,因为它允许在后台建立bitmap representation,而不是在主线程。默认“是”。
if (self.automaticallyInflatesResponseImage) {
return AFInflatedImageFromResponseWithDataAtScale((NSHTTPURLResponse *)response, data, self.imageScale);
} else {
//否则就线程安全的使用imageWithData方法得到image对象
return AFImageWithDataAtScale(data, self.imageScale);
}
#else
// Ensure that the image is set to it's correct pixel width and height
NSBitmapImageRep *bitimage = [[NSBitmapImageRep alloc] initWithData:data];
NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize([bitimage pixelsWide], [bitimage pixelsHigh])];
[image addRepresentation:bitimage];
return image;
#endif
return nil;
}
AFCompoundResponseSerializer
可以添加一组responseSerializer
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
for (id <AFURLResponseSerialization> serializer in self.responseSerializers) {
//如果是AFHTTPResponseSerializer则调到下一个
if (![serializer isKindOfClass:[AFHTTPResponseSerializer class]]) {
continue;
}
NSError *serializerError = nil;
//根据类型指定去解析
id responseObject = [serializer responseObjectForResponse:response data:data error:&serializerError];
if (responseObject) {
if (error) {
*error = AFErrorWithUnderlyingError(serializerError, *error);
}
return responseObject;
}
}
return [super responseObjectForResponse:response data:data error:error];
}
AFNetworkReachabilityManager
AFNetworkReachabilityManager
是用于监控网络状况的,来看下写法:
AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
// 设置networkReachabilityStatusBlock,根据返回的网络状态处理
[manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
NSLog(@"network status '%@'", AFStringFromNetworkReachabilityStatus(status));
}];
// 启动网络监听
[manager startMonitoring];
//首先是创建一个单例
+ (instancetype)sharedManager {
static AFNetworkReachabilityManager *_sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedManager = [self manager];
});
return _sharedManager;
}
+ (instancetype)manager
{
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
struct sockaddr_in6 address;
bzero(&address, sizeof(address));
address.sin6_len = sizeof(address);
address.sin6_family = AF_INET6;
#else
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_len = sizeof(address);
address.sin_family = AF_INET;
#endif
//获取internet环境下套接字的地址
return [self managerForAddress:&address];
}
+ (instancetype)managerForAddress:(const void *)address {
//SCNetworkReachability API允许应用程序确定系统当前网络的状态配置和目标主机的可达性,当网络状态发送改变时可以获取到通知
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address);
AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
CFRelease(reachability);
return manager;
}
- (void)startMonitoring {
[self stopMonitoring];
if (!self.networkReachability) {
return;
}
__weak __typeof(self)weakSelf = self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
strongSelf.networkReachabilityStatus = status;
if (strongSelf.networkReachabilityStatusBlock) {
strongSelf.networkReachabilityStatusBlock(status);
}
};
//SCNetworkReachability包含指定数据的结构和回调,Block_copy,Block_release
SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
//分配一个target给到client,当目标的可达性变化时接收回调
SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
// Schedules the given target with the given run loop and mode
SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
//放入后台线程执行,改变则发送通知
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) {
AFPostReachabilityStatusChange(flags, callback);
}
});
}
网友评论