iOS DevelopmentHTTPiOS Developer

在App中通过IP直连的方式访问API

2017-01-02  本文已影响0人  CSJohn

一直有用户反映,不管通过通过手机端、还是PC端访问我们的产品都会不定时出现域名劫持的问题。为了解决这个问题,我们只能绕过传统的运营商域名解析,通过IP直接访问服务。本文对App中集成HttpDNS作简要介绍。

一、HTTPDNS介绍:

httpDNS是阿里提供的面向移动端的域名解析产品,提供了面向移动端的SDK,客户端可以通过传入域名的方式调用,SDK会直接返回解析出的IP地址。

NSString *ip = [[HttpDnsService sharedInstance] getIpByHostAsync:Domain];

二、需求描述:

对项目中: 1.原生图片的请求, 2.H5网页中请求,分别做IP直连处理。

三、实现方案:

通过注册NSURLProtocol,拦截所有请求,过滤出相应的图片请求及H5网页请求,将请求的url中的域名替换为IP后,重新发起请求,获取到响应数据后,回调给URL Loading System。

1.拦截请求

由于原生图片和H5网页中的请求需要分开处理以便于实现通过降级开关分别控制,所以注册了两个NSURLProtocol分别处理这两项业务,具体策略为:
YH_ImageProtocol在拦截到请求后,按照URL后缀(是否包含:.jpg/.jpeg/.png/.gif)过滤出图片的URL。
YH_WebProtocol在拦截到请求后,排除图片的URL,则认为是需要拦截的请求。

2. 手动发起请求

拦截到请求后,需要根据协议分别做处理:如果是HTTP请求,使用NSURLSession重新发起请求,获取到响应的数据后,回调给URL Loaidng System。如果是HTTPS请求,由于当前请求URL的域名被替换成了IP地址,请求URL中的host也会被替换成HTTPDNS解析出来的IP,导致服务器获取到的域名为解析后的IP,无法找到匹配的证书,只能返回默认的证书或者不返回,所以会出现SSL/TLS握手不成功的错误。为了解决这个问题,我们需要hook HTTPS访问前SSL连接过程,根据网络请求头部域中的HOST信息,设置SSL连接PeerHost的值,之后根据服务器返回的证书执行验证过程。所以在拦截网络请求后,使用CFHTTPMessageRef创建NSInputStream实例进行Socket通信,并设置其kCFStreamSSLPeerName的值:

// 创建CFHTTPMessage对象的输入流
CFReadStreamRef readStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault,cfrequest);
inputStream = (__bridge_transfer NSInputStream *) readStream;
    
// 设置SNI host信息
NSString *host = [curRequest.allHTTPHeaderFields objectForKey:@"host"];
    if (!host) {
        host = curRequest.URL.host;
    }
    [inputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL forKey:NSStreamSocketSecurityLevelKey];
    NSDictionary *sslProperties = [[NSDictionary alloc] initWithObjectsAndKeys:
                                   host, (__bridge id) kCFStreamSSLPeerName,
                                   nil];
    [inputStream setProperty:sslProperties forKey:(__bridge_transfer NSString *) kCFStreamPropertySSLSettings];
    [inputStream setDelegate:self];
3.重定向

当返回的StatusCode在300、400之间,且header中location字段中取出合法的URL时,用该URL初始化新的请求,在protocol内部重新执行一遍之前的流程。

四、碰到的问题及解决方法:

五、待改进的地方:

目前的业务需求是,拦截到的H5请求,全部强制转为HTTPS方式请求。这种情况下会导致一些服务端不支持HTTPS的请求失败,尤其跳转到一些第三方网站的页面。为避免该问题,我们应该提供一种容错机制,当强制使用HTTPS的方式去打开页面时,如果SSL握手失败,可以再改为HTTP的方式去请求。

上一篇下一篇

猜你喜欢

热点阅读