java 访问不到本地的proxy?
这个是 apache httpclient 的代码
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvidersetCredentials(
new AuthScope("localhost", 8080),
new UsernamePasswordCredentials("username", "password"));
CloseableHttpClient httpclient = HttpClientscustom()
setDefaultCredentialsProvider(credsProvider)build();
try {
HttpHost target = new HttpHost("wwwverisigncom", 443, "https");
HttpHost proxy = new HttpHost("localhost", 8080);
RequestConfig config = RequestConfigcustom()
setProxy(proxy)
build();
HttpGet httpget = new HttpGet("/");
httpgetsetConfig(config);
Systemoutprintln("Executing request " + httpgetgetRequestLine() + " to " + target + " via " + proxy);
CloseableHttpResponse response = httpclientexecute(target, httpget);
try {
Systemoutprintln("----------------------------------------");
Systemoutprintln(responsegetStatusLine());
EntityUtilsconsume(responsegetEntity());
} finally {
responseclose();
}
} finally {
httpclientclose();
}
因为 baidu 不给发链接,你去 apache httpcomponents 的网站,找 httpclient ,example 里面都有的
负载均衡:建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽、增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。
负载均衡说白了其实就是伴随着微服务架构的诞生的产物;过去的单体架构,前端页面发起请求,然后后台接收请求直接处理,这个时候不存在什么负载均衡;但是随着单体架构向微服务架构的演变,每个后台服务可能会部署在多台服务器上面,这个时候页面请求进来,到底该由哪台服务器进行处理呢?所以得有一个选择,而这个过程就是负载均衡;同时选择的方案有很多种,例如随机挑选一台或者一台一台轮着来,这就是负载均衡算法。
也可以通过例子来帮助自己记忆,就好比古代皇帝翻牌子,最开始皇帝只有一个妃子,那不存在翻牌子这回事,再怎么翻也只能是这一个妃子侍寝。但是随着妃子多了,就得有选择了,不能同时让所有妃子一起侍寝。
工作原理图如下:
HTTP重定向服务器是一台普通的应用服务器,其唯一个功能就是根据用户的HTTP请求计算出一台真实的服务器地址,并将该服务器地址写入HTTP重定向响应中返回给用户浏览器。用户浏览器在获取到响应之后,根据返回的信息,重新发送一个请求到真实的服务器上。DNS服务器解析到IP地址为192168874,即HTTP重定向服务器的IP地址。重定向服务器计根据某种负载均衡算法算出真实的服务器地址为192168877并返回给用户浏览器,用户浏览器得到返回后重新对192168877发起了请求,最后完成访问。
这种负载均衡方案的优点是比较简单,缺点是浏览器需要两次请求服务器才能完成一次访问,性能较差;同时,重定向服务器本身的处理能力有可能成为瓶颈,整个集群的伸缩性规模有限;因此实践中很少使用这种负载均衡方案来部署。
DNS(Domain Name System)是因特网的一项服务,它作为域名和IP地址相互映射的一个分布式数据库,能够使人更方便的访问互联网。人们在通过浏览器访问网站时只需要记住网站的域名即可,而不需要记住那些不太容易理解的IP地址。在DNS系统中有一个比较重要的的资源类型叫做主机记录也称为A记录,A记录是用于名称解析的重要记录,它将特定的主机名映射到对应主机的IP地址上。如果你有一个自己的域名,那么要想别人能访问到你的网站,你需要到特定的DNS解析服务商的服务器上填写A记录,过一段时间后,别人就能通过你的域名访问你的网站了。DNS除了能解析域名之外还具有负载均衡的功能,下面是利用DNS工作原理处理负载均衡的工作原理图:
由上图可以看出,在DNS服务器中应该配置了多个A记录,如:
wwwwoshuaiqicom IN A 192168875;
wwwwoshuaiqicom IN A 192168876;
wwwwoshuaiqicom IN A 192168877;
因此,每次域名解析请求都会根据对应的负载均衡算法计算出一个不同的IP地址并返回,这样A记录中配置多个服务器就可以构成一个集群,并可以实现负载均衡。上图中,用户请求wwwwoshuaiqicom,DNS根据A记录和负载均衡算法计算得到一个IP地址192168877,并返回给浏览器,浏览器根据该IP地址,访问真实的物理服务器192168877。所有这些操作对用户来说都是透明的,用户可能只知道wwwwoshuaiqicom这个域名。
DNS域名解析负载均衡有如下优点:
同时,DNS域名解析也存在如下缺点:
请求过程:
用户发来的请求都首先要经过反向代理服务器,服务器根据用户的请求要么直接将结果返回给用户,要么将请求交给后端服务器处理,再返回给用户。
反向代理负载均衡
优点:
缺点:
代码实现
由于不同的服务器配置不同,因此它们处理请求的能力也不同,给配置高的服务器配置相对较高的权重,让其处理更多的请求,给配置较低的机器配置较低的权重减轻期负载压力。加权轮询可以较好地解决这个问题。
1思路
根据权重的大小让其获得相应被轮询到的机会。
可以根据权重我们在内存中创建一个这样的数组{s1,s2,s2,s3,s3,s3},然后再按照轮询的方式选择相应的服务器。
2缺点:请求被分配到三台服务器上机会不够平滑。前3次请求都不会落在server3上。
Nginx实现了一种平滑的加权轮询算法,可以将请求平滑(均匀)的分配到各个节点上。
代码实现
代码实现
思路:这里我们是利用区间的思想,通过一个小于在此区间范围内的一个随机数,选中对应的区间(服务器),区间越大被选中的概率就越大。
已知:
s1:[0,1] s2:(1,3] s3 (3,6]
代码实现
代码实现
REST(Representational State Transfer)表象化状态转变(表述性状态转变),基于HTTP、URI、XML、JSON等标准和协议,支持轻量级、跨平台、跨语言的架构设计。是Web服务的一种新的架构风格(一种思想)。
符合上述REST原则的架构方式称为RESTful
在Restful之前的操作:
http://127001/user/query/1 GET 根据用户id查询用户数据
http://127001/user/save POST 新增用户
http://127001/user/update POST 修改用户信息
http://127001/user/delete/1 GET/POST 删除用户信息
RESTful用法:
http://127001/user/1 GET 根据用户id查询用户数据
http://127001/user POST 新增用户
http://127001/user PUT 修改用户信息
http://127001/user DELETE 删除用户信息
之前的操作是没有问题的,大神认为是有问题的,有什么问题呢你每次请求的接口或者地址,都在做描述,例如查询的时候用了query,新增的时候用了save,其实完全没有这个必要,我使用了get请求,就是查询使用post请求,就是新增的请求,我的意图很明显,完全没有必要做描述,这就是为什么有了restful
幂等性:对同一REST接口的多次访问,得到的资源状态是相同的。
安全性:对该REST接口访问,不会使用服务器端资源的状态发生改变。
SpringMVC原生态的支持了REST风格的架构设计
所涉及到的注解:
---@RequestMapping
---@PathVariable
---@ResponseBody
传统情况下在java代码里访问restful服务,一般使用Apache的HttpClient。不过此种方法使用起来太过繁琐。spring提供了一种简单便捷的模板类来进行操作,这就是RestTemplate。
定义一个简单的restful接口
使用RestTemplate访问该服务
从这个例子可以看出,使用restTemplate访问restful接口非常的 简单粗暴无脑 。(url, requestMap, ResponseBeanclass)这三个参数分别代表 请求地址、请求参数、HTTP响应转换被转换成的对象类型。
RestTemplate方法的名称遵循命名约定,第一部分指出正在调用什么HTTP方法,第二部分指示返回的内容。本例中调用了restTemplatepostForObject方法,post指调用了HTTP的post方法,Object指将HTTP响应转换为您选择的 对象类型 。
RestTemplate定义了36个与REST资源交互的方法,其中的大多数都对应于HTTP的方法。其实,这里面只有11个独立的方法,其中有十个有三种重载形式,而第十一个则重载了六次,这样一共形成了36个方法。
实际上,由于Post 操作的非幂等性,它几乎可以代替其他的CRUD操作
目前主流的负载方案分为以下两种:
Ribbon 是一个基于 HTTP和TCP的客户端负载均衡工具。通过 Spring Cloud 的封装,可以让我们轻松地将面向服务的 REST 模版请求自动转换成客户端负载均衡的服务调用。
Spring Cloud Ribbon 虽然只是一个工具类框架,它不像服务注册中心、配置中心、API 网关那样需要独立部署,但是它几乎存在于每一个 Spring Cloud 构建的微服务和基础设施中。因为微服务间的调用,API 网关的请求转发等内容,实际上都是通过 Ribbon 来实现的(https://githubcom/Netflix/ribbon)。
Ribbon主要提供:
Ribbon模块介绍:
与Nginx的对比
应用场景的区别:
1先创建两个服务,用于负载均衡
Server 1 和Server2 的端口号要不同,不然起不来
Server 1接口如下:
Server 2接口如下:
启动类都是一样的,如下:
2创建一个调用方来请求这个接口
引依赖包
配置启动类,并注入 RestTemplate
配置一下 applicationproperties,如下:
3验证
再创建一个 测试方法来验证是否生效,放在test 目录下面,代码如下:
先启动 两个server ,然后在 测试 测试类 ,结果如下:
从结果可以看出实现了负载均衡,默认是 轮询策略,Client1和 clien2 依次调用。
Ribbon 中有两种和时间相关的设置,分别是请求连接的超时时间和请求处理的超时时间,设置规则如下:
Ribbon可以通过下面的配置项,来限制httpclient连接池的最大连接数量、以及针对不同host的最大连接数量。
负载均衡的核心,是通过负载均衡算法来实现对目标服务请求的分发。Ribbion中默认提供了7种负载均衡算法:
验证方法:
1在BaseLoadBalancerchooseServer()方法中加断点
2在RandomRulechoose()方法增加断点,观察请求是否进入。
自定义负载均衡的实现主要分几个步骤:
ILoadBalancer 接口实现类做了以下的一些事情:
修改applicationproperties文件
在ribbon负载均衡器中,提供了ping机制,每隔一段时间,就会去ping服务器,由 comnetflixloadbalancerIPing 接口去实现。
单独使用ribbon,不会激活ping机制,默认采用DummyPing(在RibbonClientConfiguration中实例化),isAlive()方法直接返回true。
ribbon和eureka集成,默认采用NIWSDiscoveryPing(在EurekaRibbonClientConfiguration中实例化的),只有服务器列表的实例状态为up的时候才会为Alive。
IPing中默认内置了一些实现方法如下。
在网络通信中,有可能会存在由网络问题或者目标服务异常导致通信失败,这种情况下我们一般会做容错设计,也就是再次发起请求进行重试。
Ribbon提供了一种重试的负载策略:RetryRule,可以通过下面这个配置项来实现
由于在单独使用Ribbon的机制下,并没有开启Ping机制,所以所有服务默认是认为正常的,则这里并不会发起重试。如果需要演示重试机制,需要增加PING的判断。
1引入依赖包
2创建一个心跳检查的类
3修改mall-portal中applicationproperties文件,添加自定义心跳检查实现,以及心跳检查间隔时间。
4在goods-service这个模块中,增加一个心跳检查的接口
5测试服务启动+停止,对于请求的影响变化。
LoadBalancer 是Spring Cloud自研的组件,支持WebFlux。
由于Ribbon停止更新进入维护状态,所以Spring Cloud不得不研发一套新的Loadbalancer机制进行替代。
1引入Loadbalancer相关jar包
2定义一个配置类,这个配置类通过硬编码的方式写死了goods-service这个服务的实例列表,代码如下
3创建一个配置类,注入一个LoadBalancerClient
4修改测试类
5为了更好的看到效果,修改goods-service模块,打印每个服务的端口号码。
目录
我的操作是这样的,让手机和电脑在同一个局域网内(比如连接同一个 wifi),接着在手机的wifi上设置代理,电脑使用 Charles 做代理,IP 为电脑在局域网 IP,我这边的环境,手机 IP 为 1721732117,电脑 IP 为 172173219。再设置代理端口为 8888。设置代理后,接下来手机的请求都会通过电脑的网卡代理请求发送出去。
其实可以不用这么绕。我之所以多设了一个代理,是因为自己电脑创建的 wifi 热点,手机接收不到。为了让手机的包能经过电脑网络嗅探到才这么处理的。
最便捷的方式,就是电脑放个 wifi 热点给手机连接完事。
创建后代理连接后,然后使用 Wireshark 嗅探网卡,比如我这里使用的是 etho0 网卡去访问网络的。这时候玩玩手机,打开几个请求,Wireshark 上面就会出现捕捉的大量的包,各种各样的协议都有,有 ARP 寻人启事(寻找 IP 对应的物理地址),有 TCP 连接包,有 HTTP 请求包。
这里我设置了一下过滤规则,把对网易的一个 https://nex163com 的一个的请求过滤出来如下:
整个完整的 HTTPS 请求的过程如下:
接下来把手机称为 A(1721732211),电脑称为 B(172173219),对完整的过程进行简要分析。
作为整个过程的第一个 TCP 包,这里对它做一个详细的剖析,理解一下 TCP 报文的格式和内容。TCP 是传输层协议,负责可靠的数据通信,它在整个体系结构的位置如下:
作为传输层协议,主要为上层协议提供三个功能:
TCP 协议为 HTTP 和 SSL 协议提供了基础的通信功能。所以 SSL 协议是基于 TCP 的。
三次握手的内容有:
对每个包进行详细的分析:
A 发出一个带 SYN 同步位的包,通知服务端要建立连接 。
第一次握手,发出的 TCP 包的数据和 Wireshark 解析的结果如下:
灰色部分就是 TCP 报文的数据内容,第一个两个字节 0x8c85 = 35973 表示源端口。
TCP 报文的格式如下,对应的如上图的灰色部分。非灰色部分分别为 IP 首部和数据帧首部。
参考谢希仁版本的 《计算机网络》一书,对照着整个报文格式表,把整个 TCP 报文的二进制信息和相关意义做些说明:
到这里的话,TCP 数据报首部固定部分结束,固定部分一共有 20 字节。也就是 TCP 首部,至少要有 20 字节。
固定首部后,就是可长度可以变化的选项了:
整个所以 TCP 数据包的大小可以这样表示:
我们 Wireshark 后面的一长串的信息就指出了该 TCP 报文的一些主要信息:
从上面的分析可以看出,这个 SYN 包并没有携带数据,但是按协议这里要消耗一个序号。
在发出 SYN 包后,A 端进入 SYN-SENT 状态。
B 收到 SYN 包,发出 SYN + ACK 确认包 。
这个包,既是确认收到了第一次握手的包,也是一个由 B 端发出的同步包,表示自己准备好了,可以开始传数据了。
TCP 报文包相对于第一次握手的包可以窥见一些变化:
可以看到,这个包的应答时间戳刚好是第一次握手的发送时间戳。从这里也可以理解到,这个包就是在响应第一次握手的包
所以,接收方 A 可以利用这个值来计算这一次 RTT ,收到第二次握手的包后,计算当前时间戳减去该包的应答时间戳就是一个 RTT 的延时了。
这虽然是 ACK 包,但也是 SYN 包,所以也要消耗一个序号。
在发出这个包后,B 端进入 SYN-REVD 状态。
A 收到后,再发出一个 ACK 确认包
发出的包如下:
这里我们产生一个疑问,这里发送端 A 发连接请求信息、接收端 B 发确认信息,又互相同步了序号,是不是已经可以传输数据了?但实际上 A 还要再发一个 ACK 确认报文,如图所示,确认收到了 B 第二次握手发出的包,这个时候,在这个 ACK 包后 A 和 B 才正式进入 ESTABLISHED 状态。这就是第三次握手。
这是为什么呢?
假设我们用两次握手,然后在第一次握手期间,A 发了第一次握手包后出现了这样的场景:一直没有得到响应而进行超时重传,又发了一次包,然后我们称上一次包为失效包。
然后我们可以看到:
所以,只有接收端 B 在发送端 A 发出了第三次握手包后,才认为连接已经建立,开始等待发送端 A 发送的数据,才不会因为失效的连接请求报文导致接收端异常。
TCP 三次握手的时序图如下:
三次握手,有几个重要的任务,一个是 同步序号 ,接收端和发送端都发出同步包来通知对方初始序号,这样子后面接收的包就可以根据序号来保证可靠传输;另一个是让发送端和接收都 做好准备 。然后就开始传数据了。
整个过程都发生在 HTTP 报文发出之前。HTTP 协议就是依靠着 TCP 协议来做传输的管理。TCP 可以认为是它的管家,管理着传输的大大小小的事务,比如要不要保证包顺序一致?什么时候发包?要不要收包?TCP 是很严格的。
三次握手在 Java API 层面,对应的就是 Socket 的连接的创建(最终调用的是 native 层的 socket 创建):
这里的 connectTimeout 对应的是三次握手的总时长,如果超时了就会被认为连接失败。
比如一个场景,客户端发出一个 SYN 报文后,迟迟没有收到服务端的 SYN + ACK。这时候客户端触发重传机制,每次重传的间隔时间加倍,同样没有收到包。然后如果这段时间超出了连接超时时间的设置,那么建立连接超时就发生了。
所以,如果三次握手要花的时间,总是大于这里的 connectTimeout 时间,这个 Socket 就无法建立连接。
我们这一次请求的三次握手时间在 180ms 左右。
像在 OkHttp 中,如果是三次握手阶段的连接超时,是会有重试机制的。也就是重新建联,重新发出 SYN 报文发起 TCP 连接。重新建联的时候会更换连接的路由,如果已经没有可选择路由的话,那么这个就真的失败了。
在 OkHttp 390 的默认配置中,连接超时的时间为 10000ms = 10s。在 OkHttpClientBuilder 中。
实际应用的时候,根据业务场景来调整。
这次请求,为了让 Wireshark 抓到手机的包,我使用了电脑作为代理。
其实就是客户端 A 使用 HTTP 协议和代理服务器 B 建立连接。和普通的 HTTP 请求一样,需要携带 IP + 端口号,如果有身份验证的时候还会带上授权信息,代理服务器 B 会使用授权信息进行验证。然后代理服务器会去连接远程主机,连接成功后返回 200。
Wireshark 抓到的包有这样两条信息,就是在创建代理:
请求报文:
响应报文:
HTTP CONNECT 是在 HTTP11 新增的命令,用于支撑 https 加密。
因为我采用代理的方式抓包才有这一个步骤。如果是直接抓 PC 机上浏览器发出的 HTTPS 包,不会有这个过程。
然后我们思考一下,为什么代理服务器需要这些信息,要连接的主机名和端口号?
这是因为后面进行 SSL 加密 HTTP协议,因为代理服务器拿不到加密密钥,是无法获取到 HTTP 首部的,进而无法这个请求是要发到哪个主机的。所以,这里先使用 CONNECT 方法,把主机名和对应的端口号通知代理服务器。
这个也被称为 HTTPS SSL 隧道协议。建立这个 SSL 隧道后,这个特殊代理就会对数据进行盲转发。
SSL 整个协议实际上分两层,SSL 记录协议和其他子协议(SSL握手协议,SSL改变密码协议,SSL警告协议):
这两层协议的关系,其实就是数据封装的关系,SSL 握手封装协议封装其他上层协议。
封装握手协议:
封装应用数据协议,比如 HTTP:
封装交换密码协议:
封装警报协议:
所以 SSL 记录协议其实就是一个其他协议的载体,只是提供了一个封装的功能。它的格式为:
MAC 就是消息验证码,用来验证数据的完整性,保证中途没有篡改。这个消息验证码比数字签名弱一些,使用的是对称密钥加密摘要。数字签名使用的是非对称密钥加密,有区分公钥私钥。
记录协议的主要目的有这几个,为其他 SSL 子协议提供了以下服务:
TCP 三次握手结束并且和代理服务器成功连接后,建联成功,客户端 A 就开始发起 SSL 连接,首先会进入 SSL 握手阶段。
SSL 握手阶段的主要目的有这么几个:
SSL 握手的流程并不是一成不变的,根据实际的应用场景来。主要有三种:
SSL 握手的完整的交互过程如下,这里是验证服务端又验证了客户端的情况:
我们的请求只验证服务端,所以 7,8,9 是不存在的。
现在具体分析每一个阶段的内容。
Client Hello
作为 SSL 握手的第一个握手包,我们详细分析和理解一下包的内容。
下面是 Wireshark 解析好的这个 SSL 协议的数据包:
这个包如何解读,按照之前对 SSL 协议的分析,其实分成两个部分:
因为是握手过程,密钥还没协商,这里还是使用明文传输,记录协议的数据载体就是明文的 SSL 握手协议。
SSL 握手协议的格式为:
我们可以从握手协议的数据包中得到这些信息:
密码套件随着密码学的发展而发展,而且根据现实应用中,可能会有某些密码被破解,从而导致密码套件可能会导致安全问题,所以一般都会使用当前最新最安全的密码套件。
在 Android 系统中,一般情况下,使用 SSLSocket进行连接的时候,会带上系统默认的支持的密码套件。但是这个有个缺点,比如某些密码套件的加密算法被破解或者出现安全漏洞,而且要跟着系统升级反应缓慢。OkHttp 在进行 SSL 握手的时候,会使用 ConnectionSpec 类中带上提供了一系列最新的密码套件。可以从注释上看,这些密码套件在 Chrome 51 和 Android 70 以上得到了完全支持。
然后,再把这些密码套件和 Android 系统支持的密码套件取交集,提交给服务端。这样,万一哪个密码套件有问题,OkHttp 官方会下降支持。网络库 OkHttp 库会随着版本的迭代,不断地去提供比较新的密码套件,并且放弃那些不安全的密码套件。接入应用即时更新 OkHttp,就不用等待缓慢的系统更新了。
如果提供的所有密码套件服务端都不支持,OkHttp 有回退机制,退而求其次,选比较旧的套件。
Server Hello
服务端收到了客户端的 Hello,通过客户端的配置信息,结合服务端的自身情况,给出了最终的配置信息。
Wireshark 解析后的内容如下:
具体内容如下:
Certificate
上面的 Server Hello 已经制定了接下来的非对称加密算法
服务端下发证书,客户端验证服务端的身份,并且取出证书携带的公钥,这个公钥是交换加密算法的公钥。也就是在 Server Hello 阶段指定的 ECDHE (EC Diffie-Hellman)算法,也是通常说的 DH 加密。
这个 Certificate 消息下发了从携带自己公钥的数字证书和 CA 证书的证书链,在 Certificates 字段中:
CA 是 PKI 体系的重要组成部分,称为认证机构。
那什么是 CA 证书?就是用来 CA 中心发布的,认证该服务单证书的合法性,可以确保该证书来源可靠而不是被中间人替换了。但是 CA 证书也可能被中间人拦截造假?那就再用一个证书来认证它。看起来好像没完没了。实际上到最后有一个根 CA 证书,这个证书存储再浏览器或者操作系统中,是系统直接信任的。
服务端证书需要 CA 证书做认证。使用的还是数字签名方式,从数据中摘要一段信息,用 CA 证书的加密。然后验证的时候时候,用 CA 证书的公钥解密,用同样的摘要算法摘要数据部分和解密好的信息进行比较。
客户端在验证服务端证书的有效性有这样的一个过程。首先会找到该证书的认证证书,也就是中级 CA 证书。然后找中级 CA 证书的认证证书,可以是另一个中级 CA 证书,也可能是根 CA 证书。这样直到根 CA 证书。
接着从根 CA 证书开始往下去验证数字签名。比如有这样的证书链:根 CA 证书-> 中级 CA 证书 -> 服务端证书。用 CA 证书的公钥去验证中级证书的数字签名,再用中级证书的公钥去验证服务器证书的数字签名。任何一个环节验证失败,就可以认为证书不合法。
这就是整个证书链的认证过程:
查看抓到的包的数据,发现只有两个证书。为服务端证书和中级 CA 证书。根 CA 证书呢?顺藤摸瓜找到它。
首先看服务端证书。它内容如下:
从这个证书中我们可以窥见这些信息:
首先是 signedCertificate 字段的内容,即数字证书的数据:
然后是证书颁发机构的签名信息:
从上面的 issuer 可以了解到,认证该服务器证书的 CA 证书为 GeoTrust SSL CA - G3 ,我们从 Certificates 找到对应的中级证书的内容如下(中级证书可以有好几级,我们这儿只有一级):
可以得到中级证书名为 GeoTrust SSL CA - G3 ,证书组织为 GeoTrust Inc 。
认证该 CA 证书的证书呢?还是看 issue 字段,认证证书名为 GeoTrust Global CA ,组织同样是 GeoTrust Inc 。
其实这个就是根 CA 证书。在这个请求中没有找到,但在浏览器或者操作系统可以找到。一般的浏览器和系统都会内置该 CA 证书。所以根证书是受浏览器或者操作系统信任的,无需其他证书做担保。
如果想要自己的系统再信任某些非通用的权威机构的根 CA 证书,那么就去安装它。
比如我的 Windows 系统就安装了 GeoTrust Global CA 证书:
像我们平时使用 Charles 抓 HTTPS 就是这个原理,把 Charles 的 CA 证书安装在手机中,成为受信任的根 CA 证书。
基本原理就是,Charles 代理作为 SSL 隧道,并没有透明传输,而是作为一个中间人,拦截了 SSL 握手信息,修改里面的 CA 证书。仿冒手机端和真实服务端建立连接获取主密钥,然后又仿冒服务端和手机客户端建立 SSL 连接,修改服务端证书的 CA 和数字签名,这样 Charles 就可以解析到加密的 HTTP 内容了。
修改后的服务端证书如下,可以看到 issuer 被替换成了 Charles 的证书。
你的这个问题就是web访问时的跨域问题。
关于跨域就一句话:同一个ip、同一个网络协议、同一个端口,三者都满足就是同一个域,否则就是跨域问题了。
下面给你介绍一个网站,一个大神关于跨域的问题解决,可以自己进去学习下。
http://blogcsdnnet/lovesummerforever/article/details/38052213
初看OkHttp源码,由于对Address、Route、Proxy、ProxySelector、RouteSelector等理解不够,读源码非常吃力,看了几遍依然对于寻找复用连接、创建连接、连接服务器、连接代理服务器、创建隧道连接等逻辑似懂非懂,本篇决定梳理一遍相关的概念及基本原理。
● HTTP/11(HTTPS)
● HTTP/2
● SPDY
一个http请求的流程(直连):
1、输入url及参数;
2、如果是url是域名则解析ip地址,可能对应多个ip,如果没有指定端口,则用默认端口,http请求用80;
3、创建socket,根据ip和端口连接服务器(socket内部会完成3次TCP握手);
4、socket成功连接后,发送http报文数据。
一个https请求的流程(直连):
1、输入url及参数;
2、如果是url是域名则解析ip地址,可能对应多个ip,如果没有指定端口,则用默认端口,https请求用443;
3、创建socket,根据ip和端口连接服务器(socket内部会完成3次TCP握手);
4、socket成功连接后进行TLS握手,可通过java标准款提供的SSLSocket完成;
5、握手成功后,发送https报文数据。
1、分类
● HTTP代理:普通代理、隧道代理
● SOCKS代理:SOCKS4、SOCKS5
2、HTTP代理分类及说明
普通代理
HTTP/11 协议的第一部分。其代理过程为:
● client 请求 proxy
● proxy 解析请求获取 origin server 地址
● proxy 向 origin server 转发请求
● proxy 接收 origin server 的响应
● proxy 向 client 转发响应
其中proxy获取目的服务器地址的标准方法是解析 request line 里的 request-URL。因为proxy需要解析报文,因此普通代理无法适用于https,因为报文都是加密的。
隧道代理
通过 Web 代理服务器用隧道方式传输基于 TCP 的协议。
请求包括两个阶段,一是连接(隧道)建立阶段,二是数据通信(请求响应)阶段,数据通信是基于 TCP packet ,代理服务器不会对请求及响应的报文作任何的处理,都是原封不动的转发,因此可以代理 HTTPS请求和响应。
代理过程为:
● client 向 proxy 发送 CONNET 请求(包含了 origin server 的地址)
● proxy 与 origin server 建立 TCP 连接
● proxy 向 client 发送响应
● client 向 proxy 发送请求,proxy 原封不动向 origin server 转发请求,请求数据不做任何封装,为原生 TCP packet
3、SOCKS代理分类及说明
● SOCKS4:只支持TCP协议(即传输控制协议)
● SOCKS5: 既支持TCP协议又支持UDP协议(即用户数据包协议),还支持各种身份验证机制、服务器端域名解析等。
SOCK4能做到的SOCKS5都可得到,但反过来却不行,比如我们常用的聊天工具QQ在使用代理时就要求用SOCKS5代理,因为它需要使用UDP协议来传输数据。
有了上面的基础知识,下面分析结合源码分析OkHttp路由相关的逻辑。OkHttp用Address来描述与目标服务器建立连接的配置信息,但请求输入的可能是域名,一个域名可能对于多个ip,真正建立连接是其中一个ip,另外,如果设置了代理,客户端是与代理服务器建立直接连接,而不是目标服务器,代理又可能是域名,可能对应多个ip。因此,这里用Route来描述最终选择的路由,即客户端与哪个ip建立连接,是代理还是直连。下面对比下Address及Route的属性,及路由选择器RouteSelector。
描述与目标服务器建立连接所需要的配置信息,包括目标主机名、端口、dns,SocketFactory,如果是https请求,包括TLS相关的SSLSocketFactory 、HostnameVerifier 、CertificatePinner,代理服务器信息Proxy 、ProxySelector 。
Route提供了真正连接服务器所需要的动态信息,明确需要连接的服务器IP地址及代理服务器,一个Address可能会有很多个路由Route供选择(一个DNS对应对个IP)。
Address和Route都是数据对象,没有提供操作方法,OkHttp另外定义了RouteSelector来完成选择的路由的操作。
1、读取代理配置信息:resetNextProxy()
读取代理配置:
● 如果有指定代理(不读取系统配置,在OkHttpClient实例中指定),则只用1个该指定代理;
● 如果没有指定,则读取系统配置的,可能有多个。
2、获取需要尝试的socket地址(目标服务器或者代理服务器):resetNextInetSocketAddress()
结合Address的host和代理,解析要尝试的套接字地址(ip+端口)列表:
● 直连或者SOCK代理, 则用目标服务器的主机名和端口,如果是HTTP代理,则用代理服务器的主机名和端口;
● 如果是SOCK代理,根据目标服务器主机名和端口号创建未解析的套接字地址,列表只有1个地址;
● 如果是直连或HTTP代理,先DNS解析,得到InetAddress列表(没有端口),再创建InetSocketAddress列表(带上端口),InetSocketAddress与InetAddress的区别是前者带端口信息。
3、获取路由列表:next()
选择路由的流程解析:
● 遍历每个代理对象,可能多个,直连的代理对象为ProxyDIRECT(实际是没有中间代理的);
● 对每个代理获取套接字地址列表;
● 遍历地址列表,创建Route,判断Route如果在路由黑名单中,则添加到失败路由列表,不在黑名单中则添加到待返回的Route列表;
● 如果最后待返回的Route列表为空,即可能所有路由都在黑名单中,实在没有新路由了,则将失败的路由集合返回;
● 传入Route列表创建Selection对象,对象比较简单,就是一个目标路由集合,及读取方法。
为了避免不必要的尝试,OkHttp会把连接失败的路由加入到黑名单中,由RouteDatabase管理,该类比较简单,就是一个失败路由集合。
1、创建Address
Address的创建在RetryAndFollowUpInteceptor里,每次请求会声明一个新的Address及StreamAllocation对象,而StreamAllocation使用Address创建RouteSelector对象,在连接时RouteSelector确定请求的路由。
每个Requst都会构造一个Address对象,构造好了Address对象只是有了与服务器连接的配置信息,但没有确定最终服务器的ip,也没有确定连接的路由。
2、创建RouteSelector
在StreamAllocation声明的同时会声明路由选择器RouteSelector,为一次请求寻找路由。
3、选择可用的路由Route
下面在测试过程跟踪实例对象来理解,分别测试直连和HTTP代理HTTP2请求路由的选择过程:
● 直连请求流程
● HTTP代理HTTPS流程
请求url: https://wwwjianshucom/p/63ba15d8877a
1、构造address对象
2、读取代理配置:resetNextProxy
3、解析目标服务器套接字地址:resetNextInetSocketAddress
4、选择Route创建RealConnection
5、确定协议
测试方法:
● 在PC端打开Charles,设置端口,如何设置代理,网上有教程,比较简单;
● 手机打开WIFI,选择连接的WIFI修改网络,在高级选项中设置中指定了代理服务器,ip为PC的ip,端口是Charles刚设置的端口;
● OkHttpClient不指定代理,发起请求。
1、构造address对象
2、读取代理配置:resetNextProxy
3、解析目标服务器套接字地址:resetNextInetSocketAddress
4、选择Route创建RealConnection
5、创建隧道
由于是代理https请求,需要用到隧道代理。
从图可以看出,建立隧道其实是发送CONNECT请求,header包括字段Proxy-Connection,目标主机名,请求内容类似:
6、确定协议,SSL握手
1、代理可分为HTTP代理和SOCK代理;
2、HTTP代理又分为普通代理和隧道代理;普通代理适合明文传输,即http请求;隧道代理仅转发TCP包,适合加密传输,即https/http2;
3、SOCK代理又分为SOCK4和SOCK5,区别是后者支持UDP传输,适合代理聊天工具如QQ;
4、没有设置代理(OkHttpClient没有指定同时系统也没有设置),客户端直接与目标服务器建立TCP连接;
5、设置了代理,代理http请求时,客户端与代理服务器建立TCP连接,如果代理服务器是域名,则解释代理服务器域名,而目标服务器的域名由代理服务器解析;
6、设置了代理,代理https/http2请求时,客户端与代理服务器建立TCP连接,发送CONNECT请求与代理服务器建立隧道,并进行SSL握手,代理服务器不解析数据,仅转发TCP数据包。
如何正确使用 HTTP proxy
OkHttp3中的代理与路由
HTTP 代理原理及实现(一)
HttpClient及有关jar包详解
1HttpClient简介
HttpClient 是 Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。本文首先介绍 HTTPClient,然后根据作者实际工作经验给出了一些常见问题的解决方法。HTTP 协议可能是现在 Internet 上使用得最多、最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源。虽然在 JDK 的 javanet 包中已经提供了访问 HTTP 协议的基本功能,但是对于大部分应用程序来说,JDK 库本身提供的功能还不够丰富和灵活。HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。HttpClient 已经应用在很多的项目中,比如 Apache Jakarta 上很著名的另外两个开源项目 Cactus 和 HTMLUnit 都使用了 HttpClient。现在HttpClient最新版本为 HttpClient 40-beta2
2HttpClient 功能介绍
以下列出的是 HttpClient 提供的主要的功能,要知道更多详细的功能可以参见 HttpClient 的主页。
(1)实现了所有 HTTP 的方法(GET,POST,PUT,HEAD 等)
(2)支持自动转向
(3)支持 HTTPS 协议
(4)支持代理服务器等
3HttpClient 基本功能的使用
(1) GET方法
使用 HttpClient 需要以下 6 个步骤:
1 创建 HttpClient 的实例
2 创建某种连接方法的实例,在这里是 GetMethod。在 GetMethod 的构造函数中传入待连接的地址
3 调用第一步中创建好的实例的 execute 方法来执行第二步中创建好的 method 实例
OkHttp 处理了很多网络疑难杂症:会从很多常用的连接问题中自动恢复。如果您的服务器配置了多个IP地址,当第一个IP连接失败的时候,OkHttp会自动尝试下一个IP。OkHttp还处理了代理服务器问题和SSL握手失败问题。
使用 OkHttp 无需重写您程序中的网络代码。OkHttp实现了几乎和javanetHttpURLConnection一样的API。如果你用了 Apache HttpClient,则OkHttp也提供了一个对应的okhttp-apache 模块。
0条评论