前言
苹果公司在全球开发者大会(WWDC)的一场安全演示会上,公布了一个最后期限——2017 年 1 月 1 日——即 App Store 当中的所有应用必须在这个日期之前启用一项名为 App Transport Security 的重要安全功能。
App Transport Security,简称 ATS,是苹果在 iOS 9 当中首次推出的一项安全功能。在启用 ATS 之后,它会强制应用通过 HTTPS(而不是 HTTP)连接网络服务,这能够通过加密来保障用户数据安全。
ATS在 iOS 9 当中是默认开启的,然而,开发者仍然能够关闭 ATS,让自己的应用通过 HTTP 连接传输数据——现在的情况是,这招在年底之后就行不通了。另外,ATS 要求使用 TLS v 1.2,但那些已经经过加密的批量数据例外,比如流媒体数据。
那么问题来了,2017年开始提交审核的app该怎么办?其实无非以下几点:
* 要求你的接口方全部支持https
* 可以添加 NSAllowsArbitraryLoads 为 YES 来禁用 ATS,一般来说,可能类似浏览器类的 app 比较容易能通过,不然等着被拒
* 选择使用 NSExceptionDomains 来针对特定的域名开放 HTTP 调用。如果是第三方接口的话更容易审核通过,如果访问的是自己的服务器的话,可能这个理由无法通过
当然还有一些问题:
* 请求只发生在公司内的网络环境,或者访问的就是局域网(难道内网那么多服务也搞个https么~) ~)
* https证书太贵了买不起(大公司应该没问题
* 万一把第三方接口加入了白名单可还是被苹果拒了。。。
想了想,应该还是有解决方案的:
* 通过统一的proxy来代理所有的请求,只需proxy部署https即可
然后问题又来了:
* proxy压力好大,又多了一个维护的东西,而且万一挂了~~~(大公司有专人负责proxy不怕)
不过不管怎么样,app总还是有发送http的需求,要是能够避开ATS限制就好了~~~
AFNetworking&ASIHTTPRequest
通过查询文档和动手实践,发现ATS只限制了NSURLConnection 和 NSURLSession 等应用层的网络接口,并没有限制直接使用CFNetwork发送http请求,比如著名的网络库AFNetworking 和 ASIHTTPRequest,新版AFNetworking已经基于NSURLSession,而ASIHTTPRequest是基于CFNetwork,通过实际测试,AFNetworking会受ATS限制而ASIHTTPRequest不会,但是直接引入ASIHTTPRequest会导致包增长,而且这个库已经很久不维护了,所以就自己动手封装一个轻量的http库吧,部分逻辑参照ASI做了精简优化,请看以下CFNetwork介绍和http请求示例代码
CFNetwork 简介
CFNetwork是一个高性能的低级框架,可以控制一些更底层的东西,如各种常用网络协议、socket通讯等,实际上除了socket是传输层之外,本质上还是应用层上的封装的通用API。使用者可以不用关心底层协议的实际细节。
(图片来源于官方文档)
目前iOS的网络编程分四层:
- WebKit:属于Cocoa层,苹果很多地方用到的页面渲染引擎;
- NSURL:也属于Cocoa层,对各类URL请求的封装;
- CFNetwork:属于Core Foundation层,基于C的封装,同样的还有CFNetServices;
- BSD sockets:属于OS层,也是基于C的封装;
框架结构
CFNetwork框架包括的类库如下:
(图片来源于官方文档)
可以看到,CFNetwork的基础是CFSocket和CFStream。
CFSocket API
Socket是网络通讯的底层基础,可以让两个socket端口互发数据。最常用的socket抽象是BSD socket了。而CFSocket则是BSD socket的抽象,基本上实现了几乎所有BSD socket的功能,并且还融入了run loop。
CFStream API
CFStream API提供了与设备无关的读写数据的方法。使用它可以为内存、文件、网络(使用socket)的数据建立stream,能使用stream而不必马上把所有数据都写入到内存中。
CFStream提供API对两种CFType对象提供抽象:CFReadStream and CFWriteStream。它同时也是CFHTTP和CFFTP的基础。
CFNetwork 版本1
//定义头信息
CFStringRef headerFieldName = CFSTR("this is a header");
CFStringRef headerFieldValue = CFSTR("value");
//创建url
CFStringRef url1 = CFSTR("http://httpbin.org/get");
CFURLRef myURL = CFURLCreateWithString(kCFAllocatorDefault, url1, NULL);
//设置请求方式
CFStringRef requestMethod = CFSTR("GET");
//创建请求
CFHTTPMessageRef myRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault,requestMethod, myURL, kCFHTTPVersion1_1);