在
web
服务器和服务器通讯的时候,使用https
链接是很是重要的,可以对数据加密传输、身份认证。https
协议须要到ca
申请证书,部署到服务器,应用端链接都是对该连接受信任的。证书可申请也能够自签,自签证书须要客户端验证经过才能访问。php
HTTP
是互联网的基础协议,默认端口80
,为知足应用需求HTTP也在不断的版本升级改进,从0.9版本
到1.1版本
功能不断的强大起来。HTTP
演变可参考:www.ruanyifeng.com/blog/2016/0…html
HTTP的特色:nginx
HTTP
客户端请求只须要肯定请求方法和路径。经常使用方法有GET、POST、HEAD
,因为对参数封装形式不同,通常获取数据使用GET
,上传数据使用POST
,GET
参数暴露在连接中,POST
则封装在请求体中Content-Type
标记传输的数据类型HTTP协议
是无状态协议,对处理过的事务无记忆能力,为了客户端服务端更好的交互,引用了Cookie
和Session
来存储请求中产生的状态工做流程:web
不须要任何处理,客户端要服务端就给。swift
HTTPS
协议为超文本传输安全协议,默认端口443
,HTTPS=HTTP+SSL
,对数据进行加密,保护数据在交互时不被窃取,提升了对服务器恶意攻击及数据假装的成本。使用该协议要求服务器申请证书并配置协议环境。api
HTTPS的工做流程:安全
须要申请证书,绑定域名,服务器中配置证书。通常我的会使用阿里的免费证书,虽然加密性通常,至少证书是受信任的。也能够本身建立证书,自建证书可使用,但不会被信任,既然有免费的咱们就走正规路线😁。bash
分别发起http和https请求:服务器
经过Charles抓包观察数据以下:
固然开启SSL代理仍是能够抓到的:
所以在作APP
的时候,为了防止APP
被抓包,咱们须要作反代理设置抓包,判断应用代理设置或证书验证。
HTTPS协议
须要到ca
申请证书,我的颁发证书是不受信任的;
HTTP
是超文本传输协议,明文传输,HTTPS
则是在HTTP
上加了一层SSL
(安全套接层),对数据加密传输;
HTTP
在服务器上的默认端为80
,HTTPS
为443端口
;
HTTP
链接是无状态的,HTTPS
协议等价于HTTP+SSL/TLS
,可进行加密传输、身份认证的网络协议。
二者的优缺点很明显,
HTTP
不存在加密,明文传输,不安全,但传输速度快,HTTPS
密文传输,传输中须要肯定应用端和服务端的保密性和数据完整性。
证书长什么样?
在ca
申请的证书,包括两个文件.key文件
和.pem文件
,通常在nginx
配置文件中配置便可。.key
是证书的私钥文件,.pem
为证书文件。
在初始化SessionManager
中能够看到,有一个初始化类型设置了ServerTrustPolicyManager
类对象,该对象就是安全策略的管理者。在实际开发中,APP可能会用到不一样的主机地址host
,根据业务不一样须要要给不一样的host
设置不一样的安全策略。所以管理者的实现以下:
open class ServerTrustPolicyManager {
public let policies: [String: ServerTrustPolicy]
public init(policies: [String: ServerTrustPolicy]) {
self.policies = policies
}
open func serverTrustPolicy(forHost host: String) -> ServerTrustPolicy? {
return policies[host]
}
}
复制代码
policies
字典,key
为host
,值为选择的响应的策略ServerTrustPolicy
serverTrustPolicy
方法经过host获取ServerTrustPolicy
对象不要问为何分离一个manager
来管理ServerTrustPolicy
,不直接使用,由于这是大厂分工要明确,责任到每一层。
ServerTrustPolicy是一个枚举类:
public enum ServerTrustPolicy {
case performDefaultEvaluation(validateHost: Bool)
case performRevokedEvaluation(validateHost: Bool, revocationFlags: CFOptionFlags)
case pinCertificates(certificates: [SecCertificate], validateCertificateChain: Bool, validateHost: Bool)
case pinPublicKeys(publicKeys: [SecKey], validateCertificateChain: Bool, validateHost: Bool)
case disableEvaluation
case customEvaluation((_ serverTrust: SecTrust, _ host: String) -> Bool)
//代码省略若干
}
复制代码
以上是类型关联,根据不一样类型作处理。
performDefaultEvaluation
默认策略类型,始终验证主机证书的有效性performRevokedEvaluation
对吊销过的证书作设置pinCertificates
验证服务器返回的证书的正确性,参数决定是否验证证书链pinPublicKeys
公钥验证disableEvaluation
无需验证,无条件信任customEvaluation
自定义验证,返回一个布尔值以上方法在项目中并无默认配置,须要咱们配置使用,常常会选择证书验证、公钥验证、不作验证模式。具体设置根据须要选择。
实践出真知,咱们发送一个https
请求看看效果。
let url = "https://www.yahibo.top/project/public/index.php?s=api/test/list"
sessionManager.request(url).response {
(response) in
print(response)
}
复制代码
运行以下:
运行一切正常,可以请求到数据,好像也没什么区别,其实上面已经作了数据传输的说明了加密验证。下面看一下抓包,开启Charles
,从新发送请求。
http
请求一切正常。
https
请求运行结果以下:
http
可以正常请求,https
确报错,这个好像是咱们想要的结果,都尚未配置相关信息😂。在AF
中便是https
在抓包状态下也是能够获取数据的,而Alamofire
好像作了更多的处理,直接避免抓包。
该安全策略其实对自签证书作的验证,既然个人证书是合法的被承认的就不作配置了😂。若是是自签证书须要对域名或证书作验证以下设置:
let policies: [String: ServerTrustPolicy] = [
"www.yahibo.top": .disableEvaluation
]
let sessionManager = SessionManager(serverTrustPolicyManager: ServerTrustPolicyManager(policies: policies))
return sessionManager
复制代码
我觉得的被验证错误,打脸😂。后续补充自签证书验证,肯定是否可以经过请求。这是一个失败的实践,不过仍是有收获的。
……
分别看一下AF
和Alamofire
的实现。
枚举类型,肯定网络状态:
typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
AFNetworkReachabilityStatusUnknown = -1,
AFNetworkReachabilityStatusNotReachable = 0,
AFNetworkReachabilityStatusReachableViaWWAN = 1,
AFNetworkReachabilityStatusReachableViaWiFi = 2,
};
复制代码
[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status){
[weakSelf setNetworkStates:status];
switch (status) {
case AFNetworkReachabilityStatusUnknown:
NSLog(@"未知网络");
break;
case AFNetworkReachabilityStatusNotReachable:
NSLog(@"这是一个不可达网络");
break;
case AFNetworkReachabilityStatusReachableViaWWAN:
NSLog(@"这是一个蜂窝网络");
break;
case AFNetworkReachabilityStatusReachableViaWiFi:
NSLog(@"这是一个WiFi网络");
break;
default:
break;
}
}];
[[AFNetworkReachabilityManager sharedManager] startMonitoring];
复制代码
网络监测无非就是以上几个状态,设置回调代码块,具体监测行为由AFNetworkReachabilityManager
完成。这里就简单看一下,下面看一下Alamofire
的网络监测,其实都是同样的调用的底层api
不变。
一样提供了这么一个类,来管理网络监测任务。对框架的学习,最大的收获就是可以更快一些的对源码作分析,快速使用框架。
具体使用
枚举类型:
public enum NetworkReachabilityStatus {
case unknown
case notReachable
case reachable(ConnectionType)
}
public enum ConnectionType {
case ethernetOrWiFi
case wwan
}
复制代码
使用:
let networkManager = NetworkReachabilityManager(host: "www.yahibo.top")
networkManager?.listener = { status in
switch status {
case .unknown:
print("未知网络")
break
case .notReachable:
print("这是一个不可达网络")
break
case .reachable(.ethernetOrWiFi):
print("这是一个WiFi网络")
break
case .reachable(.wwan):
print("这是一个蜂窝网络")
break
}
networkManager?.startListening()
复制代码
大同小异,编码方式变的更简洁了,这也符合swift
的编码风格。以上能够看出在Alamofire
中的网络监测将WiFi
网络和蜂窝网络归为一类。下面看一下源码。
一、属性
1️⃣、声明一个闭包,在外部实现呢,网络变化时掉用闭包传值
public typealias Listener = (NetworkReachabilityStatus) -> Void
复制代码
2️⃣、网络是否可达,包括蜂窝和WiFi网络
open var isReachable: Bool { return isReachableOnWWAN || isReachableOnEthernetOrWiFi }
复制代码
3️⃣、蜂窝网络是否为可达的
open var isReachableOnWWAN: Bool { return networkReachabilityStatus == .reachable(.wwan) }
复制代码
4️⃣、经过WiFi链接网络
open var isReachableOnEthernetOrWiFi: Bool { return networkReachabilityStatus == .reachable(.ethernetOrWiFi) }
复制代码
5️⃣、获取网络类型
open var networkReachabilityStatus: NetworkReachabilityStatus {
guard let flags = self.flags else { return .unknown }
return networkReachabilityStatusForFlags(flags)
}
复制代码
6️⃣、设置监听闭包在哪一个队列中调用你,默认给主队列
open var listenerQueue: DispatchQueue = DispatchQueue.main
复制代码
7️⃣、定一个闭包类型的监听属性
open var listener: Listener?
复制代码
初始化
public convenience init?(host: String) {
guard let reachability = SCNetworkReachabilityCreateWithName(nil, host) else { return nil }
self.init(reachability: reachability)
}
复制代码
ip
,传入其余只要不为空也是能够获取到网络类型,仍是设置为咱们经常使用的服务地址比较好AF
中是同样的SCNetworkReachability
对象,网络地址或名称的句柄 SCNetworkReachabilityRef
该对象能够肯定当前主机的网络状态和目标地址的可达性,提供同步或异步接口,获取当前的网络状态。以上初始化有调用了一个init
初始化方法:
private init(reachability: SCNetworkReachability) {
self.reachability = reachability
// Set the previous flags to an unreserved value to represent unknown status
self.previousFlags = SCNetworkReachabilityFlags(rawValue: 1 << 30)
}
复制代码
二、启动监听网络
networkManager?.startListening()
内部实现:
open func startListening() -> Bool {
var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
context.info = Unmanaged.passUnretained(self).toOpaque()
let callbackEnabled = SCNetworkReachabilitySetCallback(
reachability,
{ (_, flags, info) in
let reachability = Unmanaged<NetworkReachabilityManager>.fromOpaque(info!).takeUnretainedValue()
reachability.notifyListener(flags)
},
&context
)
let queueEnabled = SCNetworkReachabilitySetDispatchQueue(reachability, listenerQueue)
listenerQueue.async {
guard let flags = self.flags else { return }
self.notifyListener(flags)
}
return callbackEnabled && queueEnabled
}
复制代码
SCNetworkReachabilitySetCallback
监听网络状态的变化,发生改变即调用该回调方法listenerQueue
在开启监听是调用一次notifyListener
通知Listener闭包
func notifyListener(_ flags: SCNetworkReachabilityFlags) {
guard previousFlags != flags else { return }
previousFlags = flags
listener?(networkReachabilityStatusForFlags(flags))
}
复制代码
flags
listener闭包
向外传递当前的网络状态func networkReachabilityStatusForFlags(_ flags: SCNetworkReachabilityFlags) -> NetworkReachabilityStatus {
guard isNetworkReachable(with: flags) else { return .notReachable }
var networkStatus: NetworkReachabilityStatus = .reachable(.ethernetOrWiFi)
#if os(iOS)
if flags.contains(.isWWAN) { networkStatus = .reachable(.wwan) }
#endif
return networkStatus
}
复制代码
flags
来获取当前的网络状态,首先看网络是否为可达网络func isNetworkReachable(with flags: SCNetworkReachabilityFlags) -> Bool {
let isReachable = flags.contains(.reachable)
let needsConnection = flags.contains(.connectionRequired)
let canConnectAutomatically = flags.contains(.connectionOnDemand) || flags.contains(.connectionOnTraffic)
let canConnectWithoutUserInteraction = canConnectAutomatically && !flags.contains(.interventionRequired)
return isReachable && (!needsConnection || canConnectWithoutUserInteraction)
}
复制代码
三、关闭网络监听
当结束后执行deinit
方法来调用stopListening
方法:
open func stopListening() {
SCNetworkReachabilitySetCallback(reachability, nil, nil)
SCNetworkReachabilitySetDispatchQueue(reachability, nil)
}
复制代码
其实网络监测并不复杂,只是对系统网络监测类的一个封装,设置枚举对网络状态归类,经过
listener闭包
向外发送网络监测数据。使用只须要初始化设置主机地址,而后开启监听便可。所谓的监听就是实现对网络状态监听的回调闭包,是框架和系统底层网络层的消息传递。
学习原理及对底层源码探索是颇有必要的,可以帮助咱们快速开发,快速的解决问题。加油⛽️