一、参考
https://blog.csdn.net/qincidong/article/details/82454427
https://blog.csdn.net/wang704987562/article/details/54618498
起因
使用fiddler
抓包调试第三方接口,发现怎么都抓不了。经查阅资料,整理出java 使用代理的正确姿势。
二、设置系统代理
1. 使用代码设置
// 设置 http 代理, 只能代理 http 请求
System.setProperty("http.proxyHost", "127.0.0.1");
System.setProperty("http.proxyPort", "8888");
// 设置 https 代理, 只能代理 https 请求
System.setProperty("https.proxyHost", "127.0.0.1");
System.setProperty("https.proxyPort", "8888");
// 设置socks代理协议,支持 http 和 https 请求
// 注意:如果设置了 socks代理,再设 http/https 代理,socks代理失效
System.setProperty("socksProxyHost", "127.0.0.1");
System.setProperty("socksProxyPort", "8888");
2.JVM 命令行设置
在idea run
里面VM Options
中填写参数:-DproxyHost=127.0.0.1 -DproxyPort=8888
三、java 常用 http 请求工具设置代理
1. jdk.net 包的 HttpURLConnection (默认走系统代理)
用上面提到的设置系统代理
的方法即可。
2. apache 的 httpClient (默认不走系统代理)
不走系统代理,用上面提到的设置系统代理
的方法没用,要另外设置代理。
2.1 设置代理
设置代理很简单,一行 setProxy 就搞定了。如果是 http 请求直接就能正常请求,但是如果是 https 就没那么简单了。
在 https 通信中,客户端和服务器会交换证书和公钥,以建立安全的通信通道。但是当 fiddler 作为中间人存在时,它会中断这个过程,用自己的证书来替代服务器的证书,并与客户端进行握手。
由于 fiddler 的证书并不是由受信任的证书颁发机构(CA)签发的,jdk 会拒绝这个证书,从而无法建立 https 连接。但是如果我们把 fiddler 证书导入到 jdk 里面,程序会信任这个证书,从而可以完成通信。
导入证书是方式之一,还有一种方式是 createIgnoreVerifySSL(),代码配置不验证服务端的证书。详情请看 2.2。
下述
- 2.1.1 设置代理
- 2.1.2 ~ 2.1.5 设置代理 + 配置 createIgnoreVerifySSL()
- 我个人本地环境已经导入了证书(个人记录,以免忘记了),参考设置代理那块即可
- fiddler 自动更新后,证书有可能失效,参考 2.2 移除后重新添加
2.1.1 RequestConfig 设置代理(单纯设置代理)
// RequestConfig 配置代理
HttpHost proxy = new HttpHost("localhost", 8888, "http");
RequestConfig requestConfig = RequestConfig.custom().setProxy(proxy).build();
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet("https://httpbin.org");
httpGet.setConfig(requestConfig);
CloseableHttpResponse response = httpclient.execute(httpGet);
2.1.2 RequestConfig 设置代理 + createIgnoreVerifySSL
HttpHost proxy = new HttpHost("127.0.0.1", 8888, "HTTP");
RequestConfig requestConfig = RequestConfig.custom().setProxy(proxy).build();
httpGet.setConfig(requestConfig);
// HttpClient.setSSLContext...省略代码,详见 2.1.3
2.1.3 HttpClient 设置代理 + createIgnoreVerifySSL
HttpHost proxy = new HttpHost("127.0.0.1", 8888, "HTTP");
# 1. 设置sslContext,用于https请求 2. 设置代理
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLContext(createIgnoreVerifySSL())
.setProxy(proxy)
.build();
2.1.4 HttpClient + RoutePlanner + createIgnoreVerifySSL
HttpHost proxy = new HttpHost("127.0.0.1", 8888, "HTTP");
DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy);
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLContext(createIgnoreVerifySSL())
.setRoutePlanner(routePlanner)
.build();
2.1.5 RoutePlanner (系统代理) + System.setProperty + createIgnoreVerifySSL
RoutePlannder 配置了系统代理,可以用上面第一点讲的【二、设置系统代理】来设置系统代理。
SystemDefaultRoutePlanner routePlanner = new SystemDefaultRoutePlanner(ProxySelector.getDefault());
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLContext(createIgnoreVerifySSL())
.setRoutePlanner(routePlanner)
.build();
2.2 设置代理后,发现 https 请求 TLS 握手失败,异常日志如下:
// 握手失败异常
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException:
PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to...
以下有两种解决办法,一个代码多,一个配置多,请自行选择:
下面两个方法的总结:setProxy 要么重新设置 SSLContext(代码多),要么导 fiddler 证书到 jdk 证书库(配置多)。
方法一: 创建 HttpClient 的时候 setSSLContext (代码多)
// 创建SSLContext,绕过验证
private static SSLContext createIgnoreVerifySSL() {
//TLS1.0与SSL3.0基本上没有太大的差别,可粗略理解为TLS是SSL的继承者,但它们使用的是相同的SSLContext
SSLContext sslContext = null;
try
{
sslContext = SSLContext.getInstance("TLS");
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
// 实现一个X509TrustManager接口,用于绕过验证,不用修改里面的方法
X509TrustManager trustManager = new X509TrustManager()
{
@Override
public void checkClientTrusted(
java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
String paramString) throws CertificateException
{
}
@Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
String paramString) throws CertificateException
{
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers()
{
return null;
}
};
try
{
assert sslContext != null;
sslContext.init(null, new TrustManager[]{trustManager}, null);
}
catch (KeyManagementException e)
{
e.printStackTrace();
}
return sslContext;
}
方法二: 将 fiddler 证书导入 jdk 证书库 (配置多)
- 导出 Fiddler 的根证书,并移动到
jdk/jre/lib/security
目录下
- 当前目录进入命令行,导入证书到 jdk 证书库
keytool -keystore cacerts -importcert -alias fiddlerRoot -file FiddlerRoot.cer
默认口令:changeit,回车,有【是否信任证书】提示,输入【y】即可。
- 删除证书的命令 (后续想删除可以用到,一般用不上)
keytool -delete -alias fiddlerRoot -keystore cacerts
- fiddler 自动更新后,证书有可能失效,请删除后重新导入