HTTP2.0 HTTPS学习
消除或减少不必要的网络延迟,将需要传输的数据压缩
1、HTTP2.0与 HTTP/1.1 完全语义兼容的基础上,进一步减少了网络延迟
2、HTTP2.0多路复用:
允许同时通过单一的 HTTP/2连接发起多重的请求-响应消息(即可在同一个TCP连接上双向传输数据,从而实现多流并行而不依赖多个TCP连接,实际上HTTP/2 通信都在一个连接上完成,这个连接可以承载任意数量的双向数据流)HTTP/1.1、HTTP1.0 协议中,浏览器针对同一域名的请求限制阻塞问题(浏览器客户端在同一时间,针对同一域名(浏览器或者客户端是根据domain(域名)来建立连接)的请求有一定数量限制,超过限制数目的请求会被阻塞)
多路复用即连接共享:
一个request对应一个stream并分配一个id,一个连接上可以有多个stream,每个stream的frame可以随机的混杂在一起,接收方可以根据stream id将frame再归属到各自不同的request里面。
优先级和请求依赖的机制配合才能解决关键请求被阻塞的问题 — http2.0里的每个stream都可以设置又优先级(Priority)和依赖(Dependency)。优先级高的stream会被server优先处理和返回给客户端,stream还可以依赖其它的sub streams。优先级和依赖都是可以动态调整的。
(原http1.x协议头里可以设置Connection:Keep-Alive。在header里设置Keep-Alive可以在一定时间内复用连接)
HTTP 性能优化:
HTTP/2 通过让所有数据流共用同一个连接,可以更有效地使用 TCP 连接
减少服务端的链接压力,内存占用更少,连接吞吐量更大,连接的减少使网络拥塞状况得以改善,同时慢启动时间的减少, 使拥塞和丢包恢复速度更快
3、二进制分帧
此为HTTP2.0突破 HTTP1.1 的性能限制,改进传输性能,实现低延迟和高吞吐量的关键
二进制分帧层处于应用层(HTTP/2)和传输层(TCP or UDP)之间
二进制分帧层中,HTTP/2会将所有传输的信息转换为更小的消息和帧(frame),
并对它们采用二进制格式的编码,其中HTTP1.x的首部信息被封装为HEADER frame,而相应的Request Body则封装到DATA frame里面,这也是HTTP2.0兼容HTTP1.x的关键
4、首部压缩
HTTP/1.1并不支持HTTP首部压缩,SPDY使用DEFLATE算法,而HTTP/2使用专门为首部压缩而设计的HPACK算法
5、基于HTTPS的加密协议传输
HTTP2.0使用了tls的拓展ALPN来做协议升级,可通过黑名单机制禁用了不安全的加密算法来加强安全性能,从而大大提高了传输数据的可靠性,同时HTTP2.0也支持明文HTTP传输,而SPDY强制使用HTTPS
6、服务端推送
一种在客户端请求之前发送数据的机制。
服务器推送:服务器可以对客户端的一个请求发送多个响应,
可以缓存!也让在遵循同源的情况下,不同页面之间可以共享缓存资源成为可能。
Android平台http2.0:
它只在新系统下支持,android系统webview从android4.4(KitKat)才改成基于chrome内核的(chrome内核的webview才能支持spdy和http2.0)。
okhttp是同时支持spdy和http2.0,如果使用ALPN,okhttp要求android系统5.0+(实际上,android4.4上就有了ALPN的实现,不过有bug,知道5.0才正式修复)
//数字签名技术实现了身份认证与数据完整性保证: 消息摘要与非对称加密实现
//消息摘要算法保证了数据的完整性
HTTPS:
HTTPS是HTTP over SSL/TLS,HTTP是应用层协议,TCP是传输层协议,在应用层和传输层之间,增加了一个安全套接层SSL/TLS
SSL/TLS层负责客户端和服务器之间的加解密算法协商、密钥交换、通信连接的建立
SSL(Secure Sockets Layer 安全套接层)
TLS(Transport Layer Security 传输层安全),TLS 是SSL 的标准化后的产物
X509证书链,CA根证书,RA中间机构
Https的工作原理;
1、客户端向服务器发起Https请求;
2、服务器响应并下发证书,证书包含有公钥、证书颁发机构、过期时间、对称加密算法种类等信息;
3、客户端接收到证书后,解析证书信息验证是否合法;
4、证书合法客户端解析对称加密算法种类,并生成对应的密钥(对称加密)通过公钥加密给服务器;
5、后面服务器与客户端通讯就采用对称加密进行加解密,密钥只有客户端与服务器知道,只要加密算法够安全,数据就足够安全;
Https和Http的区别:
1、HTTP 的URL 以http:// 开头,而HTTPS 的URL 以https://
HTTP协议运行在TCP之上,,HTTPS运行在SSL/TLS之上,SSL/TLS运行在TCP之上
2、HTTP 传输的内容都是明文、不安全,而HTTPS 对传输的数据进行加密、相对安全
3、HTTP 无需证书,而HTTPS 需要CA机构wosign的颁发的SSL证书
5、HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443
android https使用:
SSLContext — 负责证书管理和信任管理器,Https可以有证书也可以没有证书
HostnameVerifier — 主机名称校验
使用HttpsURLConnection进行HTTPS通信
1、TrustManger的checkServerTrusted()
2、对服务器证书域名进行强校验或者真正实现HostnameVerifier的verify()方法
使用OKHttp3.0进行HTTPS通信
public final class HTTPSUtils {
private OkHttpClient client;
public Context mContext;
/初始化HTTPS,添加信任证书
public HTTPSUtils(Context context) {
mContext = context;
X509TrustManager trustManager;
SSLSocketFactory sslSocketFactory;
final InputStream inputStream;
try {
inputStream = mContext.getAssets().open("srca.cer"); // 得到证书的输入流
try {
trustManager = trustManagerForCertificates(inputStream);//以流的方式读入证书
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{trustManager}, null);
sslSocketFactory = sslContext.getSocketFactory();
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
client = new OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory, trustManager) //https方式
.build();
} catch (IOException e) {
e.printStackTrace();
}
}
public void run() throws Exception {
Request request = new Request.Builder()
.url("https://kyfw.12306.cn/otn/")
.build();
//okhttp 异步执行request请求
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
System.out.println(response.body().string());
}
});
}
//以流的方式添加信任证书
private X509TrustManager trustManagerForCertificates(InputStream in)
throws GeneralSecurityException {
// 证书工厂
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(in);
if (certificates.isEmpty()) {
throw new IllegalArgumentException("expected non-empty set of trusted certificates");
}
// Put the certificates a key store.
char[] password = "password".toCharArray(); // Any password will work.
// 密钥库
KeyStore keyStore = newEmptyKeyStore(password);
int index = 0;
for (Certificate certificate : certificates) {
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias, certificate);
}
// Use it to build an X509 trust manager.
// 密钥管理器
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(
KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, password);
// 信任管理器
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore); //加载密钥库到信任管理器
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers));
}
return (X509TrustManager) trustManagers[0];
}
// 生成密钥库
private KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException {
try {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
// 这里添加自定义的密码,默认
InputStream in = null; // 密码文件或字符串, 'null' creates an empty key store.
keyStore.load(in, password);
return keyStore;
} catch (IOException e) {
throw new AssertionError(e);
}
}
}
有安全证书的SSLContext:
public static SSLContext getSSLContext() {
// 生成SSLContext对象
SSLContext sslContext = SSLContext.getInstance("TLS");
// 从assets中加载证书
InputStream inStream = Application.getInstance().getAssets().open("srca.cer");
// 证书工厂
CertificateFactory cerFactory = CertificateFactory.getInstance("X.509");
Certificate cer = cerFactory.generateCertificate(inStream);
// 密钥库
KeyStore kStore = KeyStore.getInstance("PKCS12");
kStore.load(null, null);
kStore.setCertificateEntry("trust", cer);// 加载证书到密钥库中
// 密钥管理器
KeyManagerFactory keyFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyFactory.init(kStore, null);// 加载密钥库到管理器
// 信任管理器
TrustManagerFactory tFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tFactory.init(kStore);// 加载密钥库到信任管理器
// 初始化
sslContext.init(keyFactory.getKeyManagers(), tFactory.getTrustManagers(), new SecureRandom());
return sslContext;
}
//没有安全证书的SSLContext:
//需要两个对象:
//SSLContext + HostnameVerifier(主机名称校验)
public static SSLContext getSLLContext() {
SSLContext sslContext = null;
try {
sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}}, new SecureRandom());
} catch (Exception e) {
e.printStackTrace();
}
return sslContext;
}
private static HostnameVerifier hostnameVerifier = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
//
URL url = new URL("http://blog.csdn.net/yanzhenjie1003");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
// 1、设置SSLSocketFoactory 需要安全证书
if (urlConnection instanceof HttpsURLConnection) { // 是Https请求
SSLContext sslContext = SSLContextUtil.getSSLContext(); // 有安全证书的SSLContext
if (sslContext != null) {
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
((HttpsURLConnection) urlConnection).setSSLSocketFactory(sslSocketFactory);
}
}
// 2、设置SSLSocketFoactory 不需要安全证书
if (urlConnection instanceof HttpsURLConnection) { // 是Https请求
SSLContext sslContext = SSLContextUtil.getSSLContext(); //没有安全证书的SSLContext:
if (sslContext != null) {
SSLSocketFactory sslSocketFactory = sslContext.getSLLContextNoCertificate(); //没有证书
((HttpsURLConnection) urlConnection).setSSLSocketFactory(sslSocketFactory);
((HttpsURLConnection) urlConnection).setHostnameVerifier(SSLContextUtil.hostnameVerifier);
}
}
// 设置属性
urlConnection.setConnectTimeout(8 * 1000);
urlConnection.setReadTimeout(8 * 1000);
int responseCode = urlConnection.getResponseCode();
if (responseCode == 200) { // 请求成功
InputStream inputStream = urlConnection.getInputStream();
// 读取结果,发送到主线程
...
inputStream.close();
}
urlConnection.disconnect();