http回顾
http 协议 是应用层协议 同时还有 ftp dns tftp smtp snmp ,是一个基于TCP/IP通信协议来传递数据.
传输的类型由Content-Type加以标记,同时具有无连接、无状态等特点
一个请求由请求行(request line)、请求头部(header)、空行和请求数据四个部分组成
现在常用的是http1 的协议。 而http2 的协议 是在1的基础上升级,增加了安全性是机遇https,增加了效率通过二进制分帧来进行数据传输
在http1.1中,客户端在同一时间,针对同一域名下的请求有一定数量的限制
而http2.0中的多路复用优化了这一性能。多路复用允许同时通过单一的http/2 连接发起多重的请求-响应消息http2 不再依赖多个TCP连接去实现多流并行了
http 2.0 连接都是持久化的,接可以承载数十或数百个流的复用,
而且使用encoder来减少需要传输的header大小的同时通讯双方各自缓存一份头部字段表,避免了重复header的传输
tcp 回顾
tcp 位于传输层
用来传输数据,常见的三次握手
ip回顾
用于标识网络中的唯一一台主机或路由器. 一般用DNS 协议提供通过域名查找 IP 地址
https 回顾
一般理解为HTTP+SSL/TLS,通过 SSL证书来验证服务器的身份,并为浏览器和服务器之间的通信进行加密。
SSL/TLS是一个安全通信框架,上面可以承载HTTP协议或者SMTP/POP3协议等。
https请求流程
1.首先客户端通过URL访问服务器建立SSL连接。
2.服务端收到客户端请求后,会将网站支持的证书信息(证书中包含公钥)传送一份给客户端。
3.客户端的服务器开始协商SSL连接的安全等级,也就是信息加密的等级。
4.客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。
5.服务器利用自己的私钥解密出会话密钥。
6.服务器利用会话密钥加密与客户端之间的通信。
回顾得知, https 会先建立ssl 连接, 请求证书, 如果证书不正确,就终止步骤,
证书的检查由系统操作,dio 没有api 可以自定义检查 ,只有一个不可信证书处理机制
用来配处理自己的证书(注意只有不可信的证书才会执行)
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {
client.badCertificateCallback=(X509Certificate cert, String host, int port){
if(cert.pem==PEM){ // Verify the certificate
return true;
}
return false;
};
};
或者添加自签名证书(其他可信任的依然可以通过)
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {
SecurityContext sc = SecurityContext();
//file is the path of certificate
sc.setTrustedCertificates(file);
HttpClient httpClient = HttpClient(context: sc);
return httpClient;
};
如果想固定的证书校验通过.
在dio 里面有一个http_certificate_pinning 插件, 用来配置固定证书
var dio = Dio(BaseOptions(baseUrl: baseUrl))
..interceptors.add(CertificatePinningInterceptor(allowedSHAFingerprints));
源码查看 CertificatePinningInterceptor
@override
Future onRequest(
RequestOptions options,
RequestInterceptorHandler handler,
) async {
try {
//已验证的跳过
if (verifiedURLs.contains(options.baseUrl)) {
return super.onRequest(options, handler);
}
//iOS问题
if (Platform.isIOS && secure != null) {
await secure;
}
//当前插件是基于 ssl_pinning_plugin 插件二次开发
//直接看android 实现部分
secure = HttpCertificatePinning.check(
serverURL: options.baseUrl,
headerHttp: options.headers.map((a, b) => MapEntry(a, b.toString())),
sha: SHA.SHA256,
allowedSHAFingerprints: _allowedSHAFingerprints,
timeout: _timeout,
);
secure?.whenComplete(() => secure = null);
final secureString = await secure ?? '';
if (secureString.contains('CONNECTION_SECURE')) {
verifiedURLs.add(options.baseUrl);
return super.onRequest(options, handler);
} else {
handler.reject(
DioError(
requestOptions: options,
error: CertificateNotVerifiedException(),
),
);
}
} on Exception catch (e) {
...省略
}
最终调用了android 的handleCheckEvent 方法
private fun handleCheckEvent(call: MethodCall, result: Result) {
//取值
val arguments: HashMap = call.arguments as HashMap
val serverURL: String = arguments.get("url") as String
val allowedFingerprints: List = arguments.get("fingerprints") as List
val httpMethod: String = arguments.get("httpMethod") as String
val httpHeaderArgs: Map = arguments.get("headers") as Map
val timeout: Int = arguments.get("timeout") as Int
val type: String = arguments.get("type") as String
//开始检查
val get: Boolean = CompletableFuture.supplyAsync { this.checkConnexion(serverURL, allowedFingerprints, httpHeaderArgs, timeout, type, httpMethod) }.get()
//返回结果
if(get) {
result.success("CONNECTION_SECURE")
}else {
result.error("CONNECTION_NOT_SECURE", "Connection is not secure", "Fingerprint doesn't match")
}
}
代码比较简单, 直接忽略中间各种调用跳转。最终调用了getFingerprint
private fun getFingerprint(httpsURL: String, connectTimeout: Int, httpHeaderArgs: Map, type: String, httpMethod: String): String { //调用java7的API 准备连接 val url = URL(httpsURL) val httpClient: HttpsURLConnection = url.openConnection() as HttpsURLConnection if (httpMethod == "Head") httpClient.setRequestMethod("HEAD"); httpHeaderArgs.forEach { key, value -> httpClient.setRequestProperty(key, value) } httpClient.connect() //获取服务器证书 val cert: Certificate = httpClient.getServerCertificates()[0] as Certificate //断开链接 httpClient.disconnect() //返回 return this.hashString(type, cert.getEncoded()) }



