更新记录
2024-01-11
在“章节一”增加nginx需要编译proxy_connect模块的提示和参考博客。
2024-01-11
在“章节四”增加502 Bad Gateway的情况处理。
前言
前段时间,在搞nginx正向代理,实现一个端口代理http和https的能力,因为之前都是用反向代理的模式配置第三方地址,这样导致nginx会启动很多端口,然后还要开通这么多端口的防火墙策略,所以为了一切从简,决定捣鼓一番。
查了很多博客,也感谢各位开发者的共享,不过在查找资料中,也觉得现有的教程或示例多少存在不足之处,或是只能代理标准端口80和443,或是能代理所有端口,但是没有演示代码怎么写,所以博主在整合百家之长后写下一点心得和示例,方便有需要的人白嫖。
最好能给博主点个免费的赞支持一下,如果有更好的想法或其他疑问,也可以在评论里说明,博主会和你一起探讨。
一、nginx版本和安装
nginx的安装本文不做说明,C站有很多详细的安装教程博客。
nginx版本和安装的模块,在使用正向代理功能时,nginx需要编译ngx_http_proxy_connect_module模块:
./nginx -V
nginx version: nginx/1.14.2
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC)
built with OpenSSL 1.1.1q 5 Jul 2022
TLS SNI support enabled
configure arguments: --prefix=/home/test/app/nginx --with-openssl=/home/test/nginx/openssl-1.1.1q --with-pcre=/home/test/nginx/pcre-8.45 --with-zlib=/home/test/nginx/zlib-1.2.12 --with-http_stub_status_module --with-http_ssl_module --with-stream
ngx_http_proxy_connect_module参考博客:
1、CentOs7 给nginx安装ngx_http_proxy_connect_module模块,配置正向代理支持https
2、NGINX编译ngx_http_proxy_connect_module及做正向代理
二、nginx配置说明
重要参数均以注释形式给出,各位也可以与其他博客对比下配置差异,也可以深究各个配置的差异影响。
# http模块
http {
# mime类型,根据自己实际需求配置,不是本文重点
include mime.types;
# 默认类型
default_type application/octet-stream;
# log格式,根据自己实际需求配置,不是本文重点
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
# 开启sendfile,根据自己实际需求配置,不是本文重点
sendfile on;
# 超时时间,根据自己实际需求配置,不是本文重点
keepalive_timeout 65;
# 开启gzip压缩,根据自己实际需求配置,不是本文重点
#gzip on;
server {
# 监听端口
listen 10086;
# DNS解析 有效时间300秒 支持IPv6协议
resolver 114.114.114.114 valid=300s ipv6=on;
# 开启代理
proxy_connect;
# 代理白名单端口,允许所有端口
proxy_connect_allow all;
# 核心配置
location / {
# scheme http和https
# http_host 目标地址和端口
# request_uri 接口请求地址
proxy_pass $scheme://$http_host$request_uri;
# 使用http 1.1
proxy_http_version 1.1;
# 开启携带域名,防止目标存在SNI情况
proxy_ssl_server_name on;
# 请求头携带目标地址
proxy_set_header Host $http_host;
}
}
}
三、java代码示例
前言中博主也有提到,少有将配置和代码结合的博文,也可能其他开发者使用的非java开发语言,本文只演示java如何使用上述配置发送post请求。
1、引入依赖
<!-- httpclient 会自动引入 httpcore commons-codec commons-logging -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<!-- 高版本测试通过,低版本未测试,请自行测试 -->
<version>4.5.3</version>
</dependency>
2、代码演示
package org.example;
import org.apache.http.HttpHost;
import org.apache.http.HttpStatus;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
/**
* @author 系统异常
* @version 1.0
* @date 2024/1/2 10:17
* @description 代理测试类
*/
public class HttpProxyTest {
public static void main(String[] args) {
// https标准443端口,根据自身情况修改
String url = "https://www.baidu.com";
// http标准80端口,根据自身情况修改
url = "http://www.baidu.com";
// https非标准443端口,根据自身情况修改
url = "https://www.baidu.com:8080";
// http非标准80端口,根据自身情况修改
url = "http://www.baidu.com:8080";
// 测试报文
String request = "{\"test\":\"123456\"}";
try {
// 信任所有证书,如果各位调用的地址都是标准的,则不需要此构造方法,直接使用 HttpClients.createDefault() 即可
SSLConnectionSocketFactory scsf = new SSLConnectionSocketFactory(SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build(), NoopHostnameVerifier.INSTANCE);
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(scsf).build();
// 请求配置
int connTimeOut = 10000;
int readTimeOut = 10000;
String proxyIp = "192.168.109.137";
int proxyPort = 10086;
RequestConfig.Builder customReqConf = getBuilder(connTimeOut, readTimeOut, proxyIp, proxyPort);
// 构建HttpPost请求
HttpPost httpPost = new HttpPost(url);
StringEntity stringEntity = new StringEntity(request);
httpPost.setEntity(stringEntity);
httpPost.setConfig(customReqConf.build());
// 执行请求
CloseableHttpResponse response = httpClient.execute(httpPost);
if (HttpStatus.SC_OK == response.getStatusLine().getStatusCode()) {
System.out.println("请求成功");
} else {
System.out.println("请求失败");
}
// 获取响应报文
String result = EntityUtils.toString(response.getEntity());
System.out.println("响应 " + result);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取自定义的RequestConfig.Builder对象
*
* @param connTimeOut 连接超时时间,单位为毫秒
* @param readTimeOut 读取超时时间,单位为毫秒
* @param proxyIp 代理服务器的IP地址
* @param proxyPort 代理服务器的端口号
* @return 自定义的RequestConfig.Builder对象
*/
private static RequestConfig.Builder getBuilder(int connTimeOut, int readTimeOut, String proxyIp, int proxyPort) {
RequestConfig.Builder customReqConf = RequestConfig.custom();
// 连接超时
customReqConf.setConnectTimeout(connTimeOut);
// 读取超时
customReqConf.setSocketTimeout(readTimeOut);
// 代理
if (proxyIp != null && proxyPort > 0) {
HttpHost proxy = new HttpHost(proxyIp, proxyPort, "http");
customReqConf.setProxy(proxy);
}
return customReqConf;
}
}
各位还可以测试下低版本的依赖,以及其他端口或形式的url,如果不兼容的情况,欢迎在评论区指出。
四、报错说明
在捣鼓过程中,出现过几种报错也列举在此,方便各位开发者对照。
1、
javx.netssL.SLHndshakexception: sn.security,validator.validatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to mequested tanget
2、
javax.net.ssl.SSLPeerUnverifiedException: Certificate for <xxx.xxx.xx.xx> doesn't match any of the subject alternative names: [*.xxx.com, xxx.com]
3、
curl -vvv -d '' -x 192.168.109.137:10086 https://xxx.xxx.com/query
* About to connect() to proxy 192.168.109.137 port 10086 (#0)
* Trying 192.168.109.137...
* Connected to 192.168.109.137 (192.168.109.137) port 10086 (#0)
* Establish HTTP proxy tunnel to xxx.xxx.com:443
> CONNECT xxx.xxx.com:443 HTTP/1.1
> Host: xxx.xxx.com:443
> User-Agent: curl/7.29.0
> Proxy-Connection: Keep-Alive
>
< HTTP/1.1 502 Bad Gateway
< Server: nginx/1.14.2
< Date: Thu, 11 Jan 2024 08:04:16 GMT
< Content-Type: text/html
< Content-Length: 173
< Connection: close
<
* Received HTTP code 502 from proxy after CONNECT
* Connection #0 to host 192.168.109.137 left intact
curl: (56) Received HTTP code 502 from proxy after CONNECT
1和2这两种都是由于SSL忽略证书的写法有问题(错误的代码没有保留,尴尬);第三种是可能是由于ipv6解析打开的问题,只需要把上面的DNS解析调整一下(resolver 114.114.114.114 valid=300s ipv6=off;)就可以了。文中信任所有证书的方法和ipv6的问题处理亲测好使的很,如果有问题,欢迎在评论区讨论!