zoukankan      html  css  js  c++  java
  • AFNetworking 原作者都无法解决的问题: 如何使用ip直接访问https网站?

    背景

    最近App似乎有报异常是DNS无法解析,尝试解决此问题.搜集到的资料很少,甚至连AFN原作者都判定这可能是一个无解的问题,参见: https://github.com/AFNetworking/AFNetworking/issues/2954,不过最终还是靠着stackoverflow上的一丁点提示,顺利找到并汇集成了一个可用的解决方案.大喜,与君共享!

    问题描述

    通过IP直接访问网站,可以解决DNS劫持问题.DNS劫持,可以通过修改电脑的host文件模拟.如果是HTTP请求,使用ip地址直接访问接口,配合header中Host字段带上原来的域名信息即可;如果是 https请求,会很麻烦,需要 Overriding TLS Chain Validation Correctly;curl 中有一个 -resolve 方法可以实现使用指定ip访问https网站,iOS中集成curl库应该也可以,不过改动太大,未验证;对于服务器IP经常变的情况,可能需要使用httpDNS服务,参见:https://www.dnspod.cn/httpdns.

    解决方案讨论

    1. 最直接的方式是允许无效的SSL证书,生产环境不建议使用;

    2.一个需要部分重写AFN源码的方法.

    • 在Info.plist中添加NSAppTransportSecurity类型Dictionary,在NSAppTransportSecurity下添加NSAllowsArbitraryLoads类型Boolean,值设为YES.这些本来是用来解决iOS9下,允许HTTP请求访问网络的,当然作用不止这些.具体原因感兴趣的自行google.

    • 给 AFURLConnectionOperation 类添加新属性:

    /** 可信任的域名,用于支持通过ip访问此域名下的https链接.
     Trusted domain, this domain for support via IP access HTTPS links.
     */
    @property(nonatomic, strong) NSMutableArray * trustHostnames;
    
    • 给 AFURLConnectionOperation 实现的代理方法: - (void)connection:(NSURLConnection *)connection
      willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge 添加添加可信任的域名的相关逻辑代码:
    - (void)connection:(NSURLConnection *)connection
    willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
    {
        if (self.authenticationChallenge) {
            self.authenticationChallenge(connection, challenge);
            return;
        }
        
        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
            SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
            
            /* 添加可信任的域名,以支持:直接使用ip访问特定https服务器.
             Add trusted domain name to support: direct use of IP access specific HTTPS server.*/
            for (NSString * trustHostname  in [self trustHostnames]) {
                serverTrust = AFChangeHostForTrust(serverTrust, trustHostname);
            }
    
        ....
    
    • 参考Apple官方文档,实现自定义的添加可信域名的函数: AFChangeHostForTrust
    static inline SecTrustRef AFChangeHostForTrust(SecTrustRef trust, NSString * trustHostname)
    {
        if ( ! trustHostname || [trustHostname isEqualToString:@""]) {
            return trust;
        }
        
        CFMutableArrayRef newTrustPolicies = CFArrayCreateMutable(
                                                                  kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
        
        SecPolicyRef sslPolicy = SecPolicyCreateSSL(true, (CFStringRef)trustHostname);
        
        CFArrayAppendValue(newTrustPolicies, sslPolicy);
        
        
    #ifdef MAC_BACKWARDS_COMPATIBILITY
        /* This technique works in OS X (v10.5 and later) */
        
        SecTrustSetPolicies(trust, newTrustPolicies);
        CFRelease(oldTrustPolicies);
        
        return trust;
    #else
        /* This technique works in iOS 2 and later, or
         OS X v10.7 and later */
        
        CFMutableArrayRef certificates = CFArrayCreateMutable(
                                                              kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
        
        /* Copy the certificates from the original trust object */
        CFIndex count = SecTrustGetCertificateCount(trust);
        CFIndex i=0;
        for (i = 0; i < count; i++) {
            SecCertificateRef item = SecTrustGetCertificateAtIndex(trust, i);
            CFArrayAppendValue(certificates, item);
        }
        
        /* Create a new trust object */
        SecTrustRef newtrust = NULL;
        if (SecTrustCreateWithCertificates(certificates, newTrustPolicies, &newtrust) != errSecSuccess) {
            /* Probably a good spot to log something. */
            
            return NULL;
        }
        
        return newtrust;
    #endif
    }
    
    • 使用AOP方法,重写 AFURLConnectionOperation 的trustHostnames属性:
        /* 使用AOP方式,指定可信任的域名, 以支持:直接使用ip访问特定https服务器.*/
        [AFURLConnectionOperation aspect_hookSelector:@selector(trustHostnames) withOptions:AspectPositionInstead usingBlock: ^(id<AspectInfo> info){
            __autoreleasing NSArray * trustHostnames = @[@"www.example.com"];
            
            NSInvocation *invocation = info.originalInvocation;
            [invocation setReturnValue:&trustHostnames];
        }error:NULL];
    
    

    此处用到的是一个 iOS AOP库,不熟悉的点这里: http://www.ios122.com/2015/08/aspects/.

  • 相关阅读:
    ios下的appium 通过ipa包名启动app demo演示
    如何在Mac上获取App Store上的ipa用于ios下的appium 自动化测试
    Unittest命令行执行测试、执行测试发现操作实例
    nodejs之querystring模块
    nodejs之url模块
    微信小程序学习Course 5 view组件、input组件、button组件
    微信小程序学习Course 3-1 JS字符串对象学习
    微信小程序学习Course 2 关于WXSS一些样式
    微信小程序学习Course 4 事件
    微信小程序学习Course 1 微信小程序基本内容
  • 原文地址:https://www.cnblogs.com/ios122/p/5156461.html
Copyright © 2011-2022 走看看