iOS-AFNetworking源码解析(三)

AFSecurityPolicy

在iOS9后苹果默认是不能使用HTTP请求的,而AFSecurityPolicy主要的作用就是验证HTTPS请求的证书的有效性。如果你还是想要使用HTTP请求,就需要在plist里面设置NSAppTransportSecurity的NSAllowsArbitraryLoads为true,当然我们首先是推荐使用HTTPS的。

AFSecurityPolicy是安全策略类,有三种SSL Pinning模式。

typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
    AFSSLPinningModeNone,
    AFSSLPinningModePublicKey,
    AFSSLPinningModeCertificate,
};

这个是证书集合,泛型里面表示了集合里面是NSData类型,表明这个是用来存证书数据的集合,这些证书根据SSL Pinning模式来和服务器进行校验,默认是没有证书的,我们需要调用+ certificatesInBundle:方法将bundle里面的证书文件转成里面是data类型的集合

@property (nonatomic, strong, nullable) NSSet <NSData *> *pinnedCertificates;
+ (NSSet *)certificatesInBundle:(NSBundle *)bundle {
    // 获取证书
  	NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."];

    NSMutableSet *certificates = [NSMutableSet setWithCapacity:[paths count]];
    // 将证书文件转成data
  	for (NSString *path in paths) {
        NSData *certificateData = [NSData dataWithContentsOfFile:path];
        [certificates addObject:certificateData];
    }

    return [NSSet setWithSet:certificates];
}

有三种初始化的方法

第一种是默认策略,AFSSLPinningModeNone

+ (instancetype)defaultPolicy {
    AFSecurityPolicy *securityPolicy = [[self alloc] init];
    securityPolicy.SSLPinningMode = AFSSLPinningModeNone;

    return securityPolicy;
}

第二种是自定义一个安全策略,然后读取cer文件放到集合里面

+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode {
    return [self policyWithPinningMode:pinningMode withPinnedCertificates:[self defaultPinnedCertificates]];
}
+ (NSSet *)defaultPinnedCertificates {
    static NSSet *_defaultPinnedCertificates = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSBundle *bundle = [NSBundle bundleForClass:[self class]];
        _defaultPinnedCertificates = [self certificatesInBundle:bundle];
    });

    return _defaultPinnedCertificates;
}

 第三种则是需要我们多传入一个证书集合

+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates {
    AFSecurityPolicy *securityPolicy = [[self alloc] init];
    securityPolicy.SSLPinningMode = pinningMode;

    [securityPolicy setPinnedCertificates:pinnedCertificates];

    return securityPolicy;
}

设置证书的时候,就是把上面初始化时传入的证书取出公钥,再把公钥保存到mutablePinnedPublicKeys集合中

- (void)setPinnedCertificates:(NSSet *)pinnedCertificates {
    _pinnedCertificates = pinnedCertificates;

    if (self.pinnedCertificates) {
        NSMutableSet *mutablePinnedPublicKeys = [NSMutableSet setWithCapacity:[self.pinnedCertificates count]];
        for (NSData *certificate in self.pinnedCertificates) {
            // 取出公钥
          	id publicKey = AFPublicKeyForCertificate(certificate);
            if (!publicKey) {
                continue;
            }
            // 将公钥存到集合
            [mutablePinnedPublicKeys addObject:publicKey];
        }
        self.pinnedPublicKeys = [NSSet setWithSet:mutablePinnedPublicKeys];
    } else {
        self.pinnedPublicKeys = nil;
    }
}

AFPublicKeyForCertificate方法里面,做了一系列操作后返回公钥,如下:

static id AFPublicKeyForCertificate(NSData *certificate) {
    id allowedPublicKey = nil;
    SecCertificateRef allowedCertificate;
    SecCertificateRef allowedCertificates[1];
    CFArrayRef tempCertificates = nil;
    SecPolicyRef policy = nil;
    SecTrustRef allowedTrust = nil;
    SecTrustResultType result;

  	// 取出证书SecCertificateRef
    allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate);
    __Require_Quiet(allowedCertificate != NULL, _out);
	
  	// 生成证书数组
    allowedCertificates[0] = allowedCertificate;
    tempCertificates = CFArrayCreate(NULL, (const void **)allowedCertificates, 1, NULL);

  	// 生成SecPolicyRef
    policy = SecPolicyCreateBasicX509();
    __Require_noErr_Quiet(SecTrustCreateWithCertificates(tempCertificates, policy, &allowedTrust), _out);
    __Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out);
	
  	// 从SecPolicyRef中取出公钥
    allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust);

_out:
    // 一些资源的释放

    return allowedPublicKey;
}

-[evaluateServerTrust:forDomain:]方法是AFSecurityPolicy类最长也是最重要的方法,它用来验证服务端是否是受信

- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
                  forDomain:(NSString *)domain
{
    // 苹果文档中表示不要隐式地信任自己签名的证书,取而代之的是应该增加自己的CA证书到受信列表里
    if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {
        NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning.");
        return NO;
    }

    // policies增加SecPolicyRef
    NSMutableArray *policies = [NSMutableArray array];
    if (self.validatesDomainName) {
        [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
    } else {
        [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
    }
	
    // 设置信任的policies应当被验证
    SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
	
    // 向系统内置的根证书验证服务端返回的证书是否合法
    if (self.SSLPinningMode == AFSSLPinningModeNone) {
        return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
    } else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
        return NO;
    }
	
    // 根据SSLPinningMode对服务端是否受信进行校验
    switch (self.SSLPinningMode) {
        case AFSSLPinningModeNone:
        default:
            return NO;
        case AFSSLPinningModeCertificate: {
            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;
            }

            // obtain the chain after being validated, which *should* contain the pinned certificate in the last position (if it's the Root CA)
            NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
            
            for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
                if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
                    return YES;
                }
            }
            
            return NO;
        }
        case AFSSLPinningModePublicKey: {
            NSUInteger trustedPublicKeyCount = 0;
            NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);

            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;
}

 

将 Visual Studio (VS) 中的 Qt 工程转换到 Qt Creator 上运行,通常需要创建并调整 `.pro` 文件来适配项目结构。以下是详细的步骤说明: --- ### **步骤一:准备环境** 1. 确保已安装 QtQt Creator,并配置好对应的编译器(如 MinGW 或 MSVC)。 2. 将 VS 的工程项目文件夹复制出来作为新项目的起点。 --- ### **步骤二:分析现有工程** - 打开现有的 VS 解决方案 (.sln),查看包含哪些源文件、头文件以及资源文件。 - 记录所有重要的依赖项,例如库路径、额外的 include 路径等信息。 --- ### **步骤三:创建 .pro 文件** `.pro` 文件用于描述整个项目的构建规则。按照以下内容编写一个新的 `projectname.pro` 文件: #### 示例模板: ```plaintext # 设置当前使用的模块 QT += core gui widgets # 指定生成目标类型 CONFIG += c++17 console debug_and_release TARGET = projectname # 定义源文件和头文件目录 SOURCES += main.cpp \ mainwindow.cpp HEADERS += mainwindow.h FORMS += mainwindow.ui RESOURCES += resources.qrc # 添加自定义库路径或包含路径(如果有) INCLUDEPATH += ./include LIBS += -L./lib -lmylibrary ``` **注释解释:** - `QT`: 声明了要用到的 Qt 模块。 - `CONFIG`: 配置选项,支持 C++ 标准版本 (`c++17`)、控制台模式及调试发布两种状态。 - `TARGET`: 最终生成的目标名称。 - `SOURCES`, `HEADERS`, `FORMS`: 分别列出所有的 cpp/h/ui 文件。 - `RESOURCES`: 如果有 qrc 文件,则需添加到这里。 - `INCLUDEPATH` & `LIBS`: 可选字段,如果项目引用外部库则补充完整路径。 --- ### **步骤四:迁移资源和其他文件** 从原来的解决方案中提取以下部分并放入新的工作空间内: 1. 所有的 C/C++ 源码文件; 2. UI 表单设计文件; 3. 图标或其他多媒体素材组成的 `.qrc` 文件; 4. 第三方动态链接库及其头文件。 确保这些资料能够准确对应上述 pro 文件里的声明位置。 --- ### **步骤五:导入至 Qt Creator 并测试** 1. 启动 Qt Creator,在菜单栏选择【打开文件或项目】 -> 导入刚刚编辑完成的 `.pro` 文件。 2. 根据提示设置正确的 Kit 组合(包括工具链、Qt 版本),然后点击“Configure Project”按钮加载。 3. 构建项目前检查是否报错;解决冲突后再尝试运行程序验证移植效果。 --- ### 注意事项 - 若原代码中有大量特定于 MFC 或者其他非标准框架的内容,则可能需要进一步修改才能兼容纯 Qt 实现方式。 - 对复杂大型系统而言,逐步拆分功能区域分别处理会更稳妥些。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值