修改代理 IP 获取设置,新增 GeoLite 本地归属地查询,修正 devtools 配置导致 ByteBuddy 重定义方法类

ClassLoader 不一致,进一步导致重定义方法内无法获取到自定义规则的问题
This commit is contained in:
2025-05-15 01:35:53 +08:00
parent ecaf0c940c
commit d56ec783a3
19 changed files with 304 additions and 291 deletions

View File

@@ -26,6 +26,7 @@ import quant.rich.emoney.entity.config.DeviceInfoConfig.DeviceInfo;
import quant.rich.emoney.interfaces.ConfigInfo;
import quant.rich.emoney.interfaces.IConfig;
import quant.rich.emoney.patch.okhttp.PatchOkHttp;
import quant.rich.emoney.patch.okhttp.PatchOkHttpRule;
import quant.rich.emoney.util.EncryptUtils;
import quant.rich.emoney.util.SpringContextHolder;
import quant.rich.emoney.util.TextUtils;
@@ -248,11 +249,11 @@ public class EmoneyRequestConfig implements IConfig<EmoneyRequestConfig> {
// 注入 OkHttp
PatchOkHttp.apply(
r -> r
PatchOkHttpRule.when()
.hostEndsWith("emoney.cn")
.or(a -> a.hostContains("emapp"))
.or(b -> b.hasHeaderName("X-Protocol-Id"))
.overrideIf("User-Agent", getOkHttpUserAgent()));
.overrideIf("User-Agent", getOkHttpUserAgent()).build());
}
/**

View File

@@ -1,21 +1,8 @@
package quant.rich.emoney.entity.config;
import java.io.IOException;
import java.net.Proxy;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
@@ -30,14 +17,10 @@ import lombok.Data;
import lombok.Getter;
import lombok.experimental.Accessors;
import okhttp3.ConnectionPool;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.BufferedSource;
import okio.GzipSource;
import okio.Okio;
import quant.rich.emoney.client.OkHttpClientProvider;
import quant.rich.emoney.interfaces.ConfigInfo;
import quant.rich.emoney.interfaces.IConfig;
@@ -77,145 +60,25 @@ public class IndexInfoConfig implements IConfig<IndexInfoConfig> {
}
public String getOnlineConfigByUrl(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.header("User-Agent", emoneyRequestConfig.getOkHttpUserAgent())
.header("Accept-Encoding", "gzip")
.header("Connection", "Keep-Alive")
.header("Cache-Control", "no-cache")
.get()
// Host 不能在 OkHttp 中直接设置(由 URL 控制)
.build();
// 发出请求
Response backendResponse = getInstance().newCall(request).execute();
if (backendResponse.body() != null) {
// 将内容复制给前端
return
new String(backendResponse.body().bytes(), "UTF-8");
} else {
return "";
synchronized (this) {
Request request = new Request.Builder()
.url(url)
.header("Cache-Control", "no-cache")
.get()
.build();
// 发出请求
Response backendResponse = OkHttpClientProvider.getInstance().newCall(request).execute();
if (backendResponse.body() != null) {
// 将内容复制给前端
return
new String(backendResponse.body().bytes(), "UTF-8");
} else {
return "";
}
}
}
private static OkHttpClient okHttpClient;
public static ConnectionPool connectionPool = new ConnectionPool(10, 5, TimeUnit.MINUTES);
public static OkHttpClient getInstance() {
if (okHttpClient == null) { //加同步安全
synchronized (OkHttpClient.class) {
if (okHttpClient == null) { //okhttp可以缓存数据....指定缓存路径
okHttpClient = new OkHttpClient.Builder()//构建器
.proxy(Proxy.NO_PROXY) //来屏蔽系统代理
.connectionPool(connectionPool)
.sslSocketFactory(getSSLSocketFactory(), getX509TrustManager())
.hostnameVerifier(getHostnameVerifier())
.connectTimeout(600, TimeUnit.SECONDS)//连接超时
.writeTimeout(600, TimeUnit.SECONDS)//写入超时
.readTimeout(600, TimeUnit.SECONDS)//读取超时
.addNetworkInterceptor(new GzipResponseInterceptor())
.build();
okHttpClient.dispatcher().setMaxRequestsPerHost(200);
okHttpClient.dispatcher().setMaxRequests(200);
}
}
}
return okHttpClient;
}
/**
* description 忽略https证书验证
*/
private static HostnameVerifier getHostnameVerifier() {
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
};
return hostnameVerifier;
}
/**
* description 忽略https证书验证
*/
private static SSLSocketFactory getSSLSocketFactory() {
try {
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, getTrustManager(), new SecureRandom());
return sslContext.getSocketFactory();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static X509TrustManager getX509TrustManager() {
X509TrustManager trustManager = null;
try {
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
}
trustManager = (X509TrustManager) trustManagers[0];
} catch (Exception e) {
e.printStackTrace();
}
return trustManager;
}
private static TrustManager[] getTrustManager() {
TrustManager[] trustAllCerts = 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[]{};
}
}
};
return trustAllCerts;
}
public static class GzipResponseInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
// 只有服务器返回了 gzip 编码才处理
if ("gzip".equalsIgnoreCase(response.header("Content-Encoding"))) {
// 原始响应体
ResponseBody body = response.body();
if (body == null) return response;
// 用 GzipSource 包装原始流,并缓冲
GzipSource gzippedResponseBody = new GzipSource(body.source());
BufferedSource unzippedSource = Okio.buffer(gzippedResponseBody);
// 构造一个新的 ResponseBody不再带 Content-Encoding/Length
ResponseBody newBody = ResponseBody.create(
unzippedSource,
body.contentType(),
-1L
);
// 去掉 Content-Encoding/Length让后续调用 body().string() 时拿到解压后的内容
return response.newBuilder()
.removeHeader("Content-Encoding")
.removeHeader("Content-Length")
.body(newBody)
.build();
}
return response;
}
}
}

View File

@@ -4,8 +4,11 @@ import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.Data;
import lombok.experimental.Accessors;
@@ -17,6 +20,9 @@ import quant.rich.emoney.client.OkHttpClientProvider;
import quant.rich.emoney.interceptor.EnumOptionsInterceptor.EnumOptions;
import quant.rich.emoney.interfaces.ConfigInfo;
import quant.rich.emoney.interfaces.IConfig;
import quant.rich.emoney.pojo.dto.IpInfo;
import quant.rich.emoney.util.CallerLockUtil;
import quant.rich.emoney.util.GeoIPUtil;
import quant.rich.emoney.validator.ProxyConfigValid;
/**
@@ -33,25 +39,39 @@ public class ProxyConfig implements IConfig<ProxyConfig> {
* 代理类型
*/
@EnumOptions("ProxyTypeEnum")
@JsonView(IConfig.Views.Persistence.class)
private Proxy.Type proxyType = Proxy.Type.DIRECT;
/**
* 代理主机
*/
@JsonView(IConfig.Views.Persistence.class)
private String proxyHost = "";
/**
* 代理端口
*/
@JsonView(IConfig.Views.Persistence.class)
private Integer proxyPort = 1;
/**
* 是否忽略 HTTPS 证书校验
*/
@JsonView(IConfig.Views.Persistence.class)
private Boolean ignoreHttpsVerification = false;
/**
* 通过代理后的 IP不做存储只做呈现
*/
private IpInfo ipInfo;
public void afterBeanInit() {
//refreshIpThroughProxy();
}
public synchronized IpInfo refreshIpThroughProxy() {
ipInfo = GeoIPUtil.getIpInfoThroughProxy(this);
return ipInfo;
}
@@ -61,7 +81,6 @@ public class ProxyConfig implements IConfig<ProxyConfig> {
* 根据配置获取代理
* @return
*/
@JsonIgnore
public Proxy getProxy() {
if (getProxyType() != null && getProxyType() != Proxy.Type.DIRECT) {
return new Proxy(getProxyType(),
@@ -70,53 +89,18 @@ public class ProxyConfig implements IConfig<ProxyConfig> {
return Proxy.NO_PROXY;
}
/**
* 测试给定的 HTTP 代理是否有效,并验证是否真的走了代理。
* @param proxy 代理
* @param ignoreHttpsVerification 是否忽略 https 证书验证
* @return 是否有效
*/
public static boolean isProxyEffective(Proxy proxy, boolean ignoreHttpsVerification) {
synchronized (ProxyConfig.class) {
// OkHttp 客户端配置
OkHttpClient client = OkHttpClientProvider.getInstance(
proxy, ignoreHttpsVerification,
builder -> builder
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS));
// 使用 httpbin.org/ip 获取当前请求的公网 IP
Request request = new Request.Builder()
.url("https://httpbin.org/ip")
.header("User-Agent", "ProxyVerifier")
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
System.out.println("Request failed with code: " + response.code());
return false;
}
String responseBody = response.body().string();
System.out.println("Response from proxy: " + responseBody);
// 可在此根据 IP 做进一步验证,例如是否与本地 IP 不同
return true;
} catch (IOException e) {
System.out.println("Proxy error: " + e.getMessage());
return false;
}
}
}
public static void main(String[] args) {
String proxyIp = "127.0.0.1";
int proxyPort = 7897;
ProxyConfig proxyConfig = new ProxyConfig();
proxyConfig.setProxyHost(proxyIp).setProxyPort(proxyPort)
.setProxyType(Proxy.Type.SOCKS)
.setIgnoreHttpsVerification(false);
boolean result = isProxyEffective(
new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(proxyIp, proxyPort)), true);
System.out.println("Proxy is usable: " + result);
IpInfo result = GeoIPUtil.getIpInfoThroughProxy(proxyConfig);
System.out.println("Proxy is usable with through-proxy ip: " + result);
}
}