修改代理 IP 获取设置,新增 GeoLite 本地归属地查询,修正 devtools 配置导致 ByteBuddy 重定义方法类
ClassLoader 不一致,进一步导致重定义方法内无法获取到自定义规则的问题
This commit is contained in:
BIN
conf/extra/GeoLite2-City.mmdb
Normal file
BIN
conf/extra/GeoLite2-City.mmdb
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 59 MiB |
@@ -2,8 +2,8 @@
|
|||||||
"isAnonymous" : true,
|
"isAnonymous" : true,
|
||||||
"username" : "emy1730978",
|
"username" : "emy1730978",
|
||||||
"password" : "ubVa0vNmD+JJC4171eLYUw==",
|
"password" : "ubVa0vNmD+JJC4171eLYUw==",
|
||||||
"authorization" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImN0eSI6IkpXVCJ9.eyJ1dWQiOjEwMTQ2MDA5MDcsInVpZCI6Mjg1ODMyMzcsImRpZCI6IjhlMjRiN2U0NjM0NTRlNWE2ODNhODE0ZGRkZDU4MGZkIiwidHlwIjo0LCJhY2MiOiI4ZTI0YjdlNDYzNDU0ZTVhNjgzYTgxNGRkZGQ1ODBmZCIsInN3dCI6MSwibGd0IjoxNzQ2ODgyODc4MTc2LCJuYmYiOjE3NDY4ODI4NzgsImV4cCI6MTc0ODYxMDg3OCwiaWF0IjoxNzQ2ODgyODc4fQ.YDG61MPnRypUC1HDGoRRzTtcx_J_tnAqM6XowXT7Jzk",
|
"authorization" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImN0eSI6IkpXVCJ9.eyJ1dWQiOjEwMTQ2MDA5MDcsInVpZCI6Mjg1ODMyMzcsImRpZCI6IjhlMjRiN2U0NjM0NTRlNWE2ODNhODE0ZGRkZDU4MGZkIiwidHlwIjo0LCJhY2MiOiI4ZTI0YjdlNDYzNDU0ZTVhNjgzYTgxNGRkZGQ1ODBmZCIsInN3dCI6MSwibGd0IjoxNzQ3MjE2MTIzNDQ2LCJuYmYiOjE3NDcyMTYxMjMsImV4cCI6MTc0ODk0NDEyMywiaWF0IjoxNzQ3MjE2MTIzfQ.fIoy5T41WUmbi7jMlVTXtWY_s8IIhNlO6LDNpXvibEc",
|
||||||
"authorizationUpdateTime" : [ 2025, 5, 10, 21, 14, 38, 649402700 ],
|
"authorizationUpdateTime" : [ 2025, 5, 14, 17, 48, 42, 794676900 ],
|
||||||
"androidId" : "7c135cebb4c94177",
|
"androidId" : "7c135cebb4c94177",
|
||||||
"androidVersion" : "13",
|
"androidVersion" : "13",
|
||||||
"androidSdkLevel" : "33",
|
"androidSdkLevel" : "33",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"username" : "admin",
|
"username" : "admin",
|
||||||
"password" : "81667f60a8c11d4c8e9d2e0670ff24667e6c72d49b0b15562525bcbd",
|
"password" : "81667f60a8c11d4c8e9d2e0670ff24667e6c72d49b0b15562525bcbd",
|
||||||
|
"email" : "huocaizhu@gmail.com",
|
||||||
"isInited" : true
|
"isInited" : true
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"proxyType" : "DIRECT",
|
"proxyType" : "HTTP",
|
||||||
"proxyHost" : "",
|
"proxyHost" : "127.0.0.1",
|
||||||
"proxyPort" : 0,
|
"proxyPort" : 8888,
|
||||||
"ignoreHttpsVerification" : true
|
"ignoreHttpsVerification" : true
|
||||||
}
|
}
|
||||||
18
pom.xml
18
pom.xml
@@ -15,7 +15,11 @@
|
|||||||
<name>EmoGrab</name>
|
<name>EmoGrab</name>
|
||||||
<description>Emoney 数据抓取平台</description>
|
<description>Emoney 数据抓取平台</description>
|
||||||
<properties>
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
<java.version>18</java.version>
|
<java.version>18</java.version>
|
||||||
|
<maven.compiler.source>18</maven.compiler.source>
|
||||||
|
<maven.compiler.target>18</maven.compiler.target>
|
||||||
</properties>
|
</properties>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -51,6 +55,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-devtools</artifactId>
|
<artifactId>spring-boot-devtools</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -272,6 +277,19 @@
|
|||||||
<artifactId>byte-buddy-agent</artifactId>
|
<artifactId>byte-buddy-agent</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- IP 归属地 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.maxmind.geoip2</groupId>
|
||||||
|
<artifactId>geoip2</artifactId>
|
||||||
|
<version>4.3.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- IP 归属地,但是纯真 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.cz88</groupId>
|
||||||
|
<artifactId>czdb-search</artifactId>
|
||||||
|
<version>1.0.2.7</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ import okhttp3.OkHttpClient;
|
|||||||
public class EmoneyClient implements Cloneable {
|
public class EmoneyClient implements Cloneable {
|
||||||
|
|
||||||
private static final String MBS_URL = "https://mbs.emoney.cn/";
|
private static final String MBS_URL = "https://mbs.emoney.cn/";
|
||||||
private static final String LOGIN_URL = "https://emapp.emoney.cn/user/auth/login";
|
//private static final String LOGIN_URL = "https://emapp.emoney.cn/user/auth/login";
|
||||||
// private static final String LOGIN_URL = "http://localhost:7790/user/auth/login";
|
private static final String LOGIN_URL = "http://localhost:7790/user/auth/login";
|
||||||
private static final String LOGIN_X_PROTOCOL_ID = "user%2Fauth%2Flogin";
|
private static final String LOGIN_X_PROTOCOL_ID = "user%2Fauth%2Flogin";
|
||||||
|
|
||||||
private static volatile EmoneyRequestConfig emoneyRequestConfig;
|
private static volatile EmoneyRequestConfig emoneyRequestConfig;
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
package quant.rich.emoney.client;
|
package quant.rich.emoney.client;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.Inet4Address;
|
||||||
|
import java.net.InetAddress;
|
||||||
import java.net.Proxy;
|
import java.net.Proxy;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.net.ssl.HostnameVerifier;
|
import javax.net.ssl.HostnameVerifier;
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
@@ -16,6 +21,7 @@ import javax.net.ssl.TrustManager;
|
|||||||
import javax.net.ssl.TrustManagerFactory;
|
import javax.net.ssl.TrustManagerFactory;
|
||||||
import javax.net.ssl.X509TrustManager;
|
import javax.net.ssl.X509TrustManager;
|
||||||
|
|
||||||
|
import okhttp3.Dns;
|
||||||
import okhttp3.Interceptor;
|
import okhttp3.Interceptor;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
@@ -30,6 +36,7 @@ import quant.rich.emoney.util.SpringContextHolder;
|
|||||||
/**
|
/**
|
||||||
* OkHttpClient 提供器
|
* OkHttpClient 提供器
|
||||||
* @see quant.rich.emoney.entity.config.ProxyConfig
|
* @see quant.rich.emoney.entity.config.ProxyConfig
|
||||||
|
* @see okhttp3.internal.http.BridgeInterceptor
|
||||||
*/
|
*/
|
||||||
public class OkHttpClientProvider {
|
public class OkHttpClientProvider {
|
||||||
|
|
||||||
@@ -61,7 +68,10 @@ public class OkHttpClientProvider {
|
|||||||
*/
|
*/
|
||||||
public static OkHttpClient getInstance(Consumer<OkHttpClient.Builder> builderConsumer) {
|
public static OkHttpClient getInstance(Consumer<OkHttpClient.Builder> builderConsumer) {
|
||||||
ProxyConfig proxyConfig = getProxyConfig();
|
ProxyConfig proxyConfig = getProxyConfig();
|
||||||
return getInstance(proxyConfig.getProxy(), proxyConfig.getIgnoreHttpsVerification(), builderConsumer);
|
return getInstance(
|
||||||
|
proxyConfig.getProxy(),
|
||||||
|
proxyConfig.getIgnoreHttpsVerification(),
|
||||||
|
builderConsumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -81,7 +91,10 @@ public class OkHttpClientProvider {
|
|||||||
* @param builderConsumer 可根据该 consumer 自定义 builder 其他参数,注意 proxy、https 校验等最终仍会根据其他参数覆盖
|
* @param builderConsumer 可根据该 consumer 自定义 builder 其他参数,注意 proxy、https 校验等最终仍会根据其他参数覆盖
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static OkHttpClient getInstance(Proxy proxy, boolean ignoreHttpsVerification, Consumer<OkHttpClient.Builder> builderConsumer) {
|
public static OkHttpClient getInstance(
|
||||||
|
Proxy proxy,
|
||||||
|
boolean ignoreHttpsVerification,
|
||||||
|
Consumer<OkHttpClient.Builder> builderConsumer) {
|
||||||
OkHttpClient.Builder builder = new OkHttpClient.Builder();
|
OkHttpClient.Builder builder = new OkHttpClient.Builder();
|
||||||
if (builderConsumer != null) {
|
if (builderConsumer != null) {
|
||||||
builderConsumer.accept(builder);
|
builderConsumer.accept(builder);
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import quant.rich.emoney.entity.config.DeviceInfoConfig.DeviceInfo;
|
|||||||
import quant.rich.emoney.interfaces.ConfigInfo;
|
import quant.rich.emoney.interfaces.ConfigInfo;
|
||||||
import quant.rich.emoney.interfaces.IConfig;
|
import quant.rich.emoney.interfaces.IConfig;
|
||||||
import quant.rich.emoney.patch.okhttp.PatchOkHttp;
|
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.EncryptUtils;
|
||||||
import quant.rich.emoney.util.SpringContextHolder;
|
import quant.rich.emoney.util.SpringContextHolder;
|
||||||
import quant.rich.emoney.util.TextUtils;
|
import quant.rich.emoney.util.TextUtils;
|
||||||
@@ -248,11 +249,11 @@ public class EmoneyRequestConfig implements IConfig<EmoneyRequestConfig> {
|
|||||||
|
|
||||||
// 注入 OkHttp
|
// 注入 OkHttp
|
||||||
PatchOkHttp.apply(
|
PatchOkHttp.apply(
|
||||||
r -> r
|
PatchOkHttpRule.when()
|
||||||
.hostEndsWith("emoney.cn")
|
.hostEndsWith("emoney.cn")
|
||||||
.or(a -> a.hostContains("emapp"))
|
.or(a -> a.hostContains("emapp"))
|
||||||
.or(b -> b.hasHeaderName("X-Protocol-Id"))
|
.or(b -> b.hasHeaderName("X-Protocol-Id"))
|
||||||
.overrideIf("User-Agent", getOkHttpUserAgent()));
|
.overrideIf("User-Agent", getOkHttpUserAgent()).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,21 +1,8 @@
|
|||||||
package quant.rich.emoney.entity.config;
|
package quant.rich.emoney.entity.config;
|
||||||
|
|
||||||
import java.io.IOException;
|
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 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.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
|
||||||
@@ -30,14 +17,10 @@ import lombok.Data;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
import okhttp3.ConnectionPool;
|
import okhttp3.ConnectionPool;
|
||||||
import okhttp3.Interceptor;
|
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
import okhttp3.ResponseBody;
|
import quant.rich.emoney.client.OkHttpClientProvider;
|
||||||
import okio.BufferedSource;
|
|
||||||
import okio.GzipSource;
|
|
||||||
import okio.Okio;
|
|
||||||
import quant.rich.emoney.interfaces.ConfigInfo;
|
import quant.rich.emoney.interfaces.ConfigInfo;
|
||||||
import quant.rich.emoney.interfaces.IConfig;
|
import quant.rich.emoney.interfaces.IConfig;
|
||||||
|
|
||||||
@@ -77,18 +60,15 @@ public class IndexInfoConfig implements IConfig<IndexInfoConfig> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getOnlineConfigByUrl(String url) throws IOException {
|
public String getOnlineConfigByUrl(String url) throws IOException {
|
||||||
|
synchronized (this) {
|
||||||
Request request = new Request.Builder()
|
Request request = new Request.Builder()
|
||||||
.url(url)
|
.url(url)
|
||||||
.header("User-Agent", emoneyRequestConfig.getOkHttpUserAgent())
|
|
||||||
.header("Accept-Encoding", "gzip")
|
|
||||||
.header("Connection", "Keep-Alive")
|
|
||||||
.header("Cache-Control", "no-cache")
|
.header("Cache-Control", "no-cache")
|
||||||
.get()
|
.get()
|
||||||
// Host 不能在 OkHttp 中直接设置(由 URL 控制)
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// 发出请求
|
// 发出请求
|
||||||
Response backendResponse = getInstance().newCall(request).execute();
|
Response backendResponse = OkHttpClientProvider.getInstance().newCall(request).execute();
|
||||||
if (backendResponse.body() != null) {
|
if (backendResponse.body() != null) {
|
||||||
// 将内容复制给前端
|
// 将内容复制给前端
|
||||||
return
|
return
|
||||||
@@ -97,125 +77,8 @@ public class IndexInfoConfig implements IConfig<IndexInfoConfig> {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private static OkHttpClient okHttpClient;
|
|
||||||
|
|
||||||
public static ConnectionPool connectionPool = new ConnectionPool(10, 5, TimeUnit.MINUTES);
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,11 @@ import java.io.IOException;
|
|||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Proxy;
|
import java.net.Proxy;
|
||||||
import java.util.concurrent.TimeUnit;
|
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.Data;
|
||||||
import lombok.experimental.Accessors;
|
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.interceptor.EnumOptionsInterceptor.EnumOptions;
|
||||||
import quant.rich.emoney.interfaces.ConfigInfo;
|
import quant.rich.emoney.interfaces.ConfigInfo;
|
||||||
import quant.rich.emoney.interfaces.IConfig;
|
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;
|
import quant.rich.emoney.validator.ProxyConfigValid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,25 +39,39 @@ public class ProxyConfig implements IConfig<ProxyConfig> {
|
|||||||
* 代理类型
|
* 代理类型
|
||||||
*/
|
*/
|
||||||
@EnumOptions("ProxyTypeEnum")
|
@EnumOptions("ProxyTypeEnum")
|
||||||
|
@JsonView(IConfig.Views.Persistence.class)
|
||||||
private Proxy.Type proxyType = Proxy.Type.DIRECT;
|
private Proxy.Type proxyType = Proxy.Type.DIRECT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 代理主机
|
* 代理主机
|
||||||
*/
|
*/
|
||||||
|
@JsonView(IConfig.Views.Persistence.class)
|
||||||
private String proxyHost = "";
|
private String proxyHost = "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 代理端口
|
* 代理端口
|
||||||
*/
|
*/
|
||||||
|
@JsonView(IConfig.Views.Persistence.class)
|
||||||
private Integer proxyPort = 1;
|
private Integer proxyPort = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否忽略 HTTPS 证书校验
|
* 是否忽略 HTTPS 证书校验
|
||||||
*/
|
*/
|
||||||
|
@JsonView(IConfig.Views.Persistence.class)
|
||||||
private Boolean ignoreHttpsVerification = false;
|
private Boolean ignoreHttpsVerification = false;
|
||||||
|
|
||||||
public void afterBeanInit() {
|
/**
|
||||||
|
* 通过代理后的 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
|
* @return
|
||||||
*/
|
*/
|
||||||
@JsonIgnore
|
|
||||||
public Proxy getProxy() {
|
public Proxy getProxy() {
|
||||||
if (getProxyType() != null && getProxyType() != Proxy.Type.DIRECT) {
|
if (getProxyType() != null && getProxyType() != Proxy.Type.DIRECT) {
|
||||||
return new Proxy(getProxyType(),
|
return new Proxy(getProxyType(),
|
||||||
@@ -70,53 +89,18 @@ public class ProxyConfig implements IConfig<ProxyConfig> {
|
|||||||
return Proxy.NO_PROXY;
|
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) {
|
public static void main(String[] args) {
|
||||||
String proxyIp = "127.0.0.1";
|
String proxyIp = "127.0.0.1";
|
||||||
int proxyPort = 7897;
|
int proxyPort = 7897;
|
||||||
|
|
||||||
boolean result = isProxyEffective(
|
ProxyConfig proxyConfig = new ProxyConfig();
|
||||||
new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(proxyIp, proxyPort)), true);
|
proxyConfig.setProxyHost(proxyIp).setProxyPort(proxyPort)
|
||||||
System.out.println("Proxy is usable: " + result);
|
.setProxyType(Proxy.Type.SOCKS)
|
||||||
|
.setIgnoreHttpsVerification(false);
|
||||||
|
|
||||||
|
IpInfo result = GeoIPUtil.getIpInfoThroughProxy(proxyConfig);
|
||||||
|
System.out.println("Proxy is usable with through-proxy ip: " + result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,16 @@ package quant.rich.emoney.patch.okhttp;
|
|||||||
|
|
||||||
import net.bytebuddy.ByteBuddy;
|
import net.bytebuddy.ByteBuddy;
|
||||||
import net.bytebuddy.agent.ByteBuddyAgent;
|
import net.bytebuddy.agent.ByteBuddyAgent;
|
||||||
|
import net.bytebuddy.agent.builder.AgentBuilder;
|
||||||
import net.bytebuddy.asm.Advice;
|
import net.bytebuddy.asm.Advice;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
|
import quant.rich.emoney.util.SpringContextHolder;
|
||||||
|
import net.bytebuddy.dynamic.ClassFileLocator;
|
||||||
|
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
|
||||||
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
|
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.*;
|
import static net.bytebuddy.matcher.ElementMatchers.*;
|
||||||
|
|
||||||
|
import java.lang.instrument.Instrumentation;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
@@ -14,12 +19,21 @@ import java.util.function.Consumer;
|
|||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
|
||||||
public class PatchOkHttp {
|
public class PatchOkHttp {
|
||||||
private static final Logger log = LoggerFactory.getLogger(PatchOkHttp.class);
|
private static final Logger log = LoggerFactory.getLogger(PatchOkHttp.class);
|
||||||
private static final List<PatchOkHttpRule> rules = new CopyOnWriteArrayList<>();
|
private static final List<PatchOkHttpRule> rules = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
private static boolean isHooked = false;
|
private static boolean isHooked = false;
|
||||||
|
|
||||||
|
public static void apply(PatchOkHttpRule rule) {
|
||||||
|
rules.add(rule);
|
||||||
|
// log.debug("apply() running in classloader {}", PatchOkHttp.class.getClassLoader());
|
||||||
|
if (!isHooked) hook();
|
||||||
|
}
|
||||||
|
|
||||||
public static void apply(PatchOkHttpRule.Builder builder) {
|
public static void apply(PatchOkHttpRule.Builder builder) {
|
||||||
rules.add(builder.build());
|
rules.add(builder.build());
|
||||||
if (!isHooked) hook();
|
if (!isHooked) hook();
|
||||||
@@ -33,6 +47,7 @@ public class PatchOkHttp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void match(RequestContext ctx, String currentHeader, Consumer<String> consumer) {
|
public static void match(RequestContext ctx, String currentHeader, Consumer<String> consumer) {
|
||||||
|
// log.debug("match() running in classloader {}", PatchOkHttp.class.getClassLoader());
|
||||||
for (PatchOkHttpRule rule : PatchOkHttp.rules) {
|
for (PatchOkHttpRule rule : PatchOkHttp.rules) {
|
||||||
if (rule.matches(ctx)) {
|
if (rule.matches(ctx)) {
|
||||||
rule.apply(ctx, currentHeader, consumer);
|
rule.apply(ctx, currentHeader, consumer);
|
||||||
@@ -40,17 +55,46 @@ public class PatchOkHttp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>通过指定 Advice 重定义指定类的指定方法<br>
|
||||||
|
* 此处为:<code>okhttp3.Request$Builder.header(String, String)</code></p>
|
||||||
|
* <p>
|
||||||
|
* <b>注意:</b>必须保证欲重定义的类的方法的 ClassLoader 和调用该方法的 ClassLoader
|
||||||
|
* 一致,否则会导致两者的静态类的静态变量不一致,<b>尤其是项目引入了 spring-boot-devtool
|
||||||
|
* 时</b>,一般调用该方法的类由 RestartClassLoader 加载,而 okhttp 包由于是 jar 包,一般交由默认
|
||||||
|
* ClassLoaders 进行加载,这就导致 PatchOkHttp 在两个 ClassLoader 中不一致,实际注入方法内 <code>PatchOkHttp.<i><b>rules</b></i></code>
|
||||||
|
* 为空,无论如何都无法命中设定的规则。</p>
|
||||||
|
* <p>解决方法:<ol>
|
||||||
|
* <li>在 src/main/resources 下建立 META-INF 目录,新建
|
||||||
|
* spring-devtools.properties,并在其内增加一行“restart.include.okhttp3=.*okhttp-.*\.jar”,指定由
|
||||||
|
* RestartClassLoader 加载 okhttp-*.jar 包;</li>
|
||||||
|
* <li>在 application.yml 中,增加需要额外扫描的路径,即设置参数“spring.devtools.restart.additional-pathes:
|
||||||
|
* lib/”,否则即便是进行了第一步,也不会生效;</li>
|
||||||
|
* <li>在方法体内,使用外部 RestartClassLoader 载入欲重定义的方法类,并在调用 ByteBuddy 时指定该方法类和 ClassLoader。</li>
|
||||||
|
* </ol>
|
||||||
|
* </p>
|
||||||
|
* @see org.springframework.boot.devtools.restart.classloader.RestartClassLoader
|
||||||
|
* @see jdk.internal.loader.ClassLoaders
|
||||||
|
*/
|
||||||
private static void hook() {
|
private static void hook() {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
ByteBuddyAgent.install();
|
ByteBuddyAgent.install();
|
||||||
|
|
||||||
new ByteBuddy()
|
ClassLoader appLoader = PatchOkHttp.class.getClassLoader();
|
||||||
.redefine(Class.forName("okhttp3.Request$Builder"))
|
Class<?> builderClass = appLoader.loadClass("okhttp3.Request$Builder");
|
||||||
|
|
||||||
|
|
||||||
|
if (builderClass.getClassLoader() != appLoader) {
|
||||||
|
int exitCode = SpringApplication.exit(
|
||||||
|
SpringContextHolder.getBean(ConfigurableApplicationContext.class), () -> 0);
|
||||||
|
System.exit(exitCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
new ByteBuddy().redefine(builderClass)
|
||||||
.visit(Advice.to(HeaderInterceptor.class)
|
.visit(Advice.to(HeaderInterceptor.class)
|
||||||
.on(named("header").and(takesArguments(String.class, String.class))))
|
.on(named("header").and(takesArguments(String.class, String.class))))
|
||||||
.make()
|
.make().load(appLoader, ClassReloadingStrategy.fromInstalledAgent());
|
||||||
.load(Class.forName("okhttp3.Request$Builder").getClassLoader(),
|
|
||||||
ClassReloadingStrategy.fromInstalledAgent());
|
|
||||||
|
|
||||||
|
|
||||||
isHooked = true;
|
isHooked = true;
|
||||||
@@ -64,7 +108,8 @@ public class PatchOkHttp {
|
|||||||
|
|
||||||
|
|
||||||
public static final Logger log = LoggerFactory.getLogger(HeaderInterceptor.class);
|
public static final Logger log = LoggerFactory.getLogger(HeaderInterceptor.class);
|
||||||
@Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
|
@Advice.OnMethodEnter(
|
||||||
|
skipOn = Advice.OnNonDefaultValue.class)
|
||||||
public static boolean intercept(
|
public static boolean intercept(
|
||||||
@Advice.This Request.Builder builder,
|
@Advice.This Request.Builder builder,
|
||||||
@Advice.Argument(0) String name,
|
@Advice.Argument(0) String name,
|
||||||
@@ -72,13 +117,8 @@ public class PatchOkHttp {
|
|||||||
@Advice.Local("call") Callable<okhttp3.Request.Builder> superCall
|
@Advice.Local("call") Callable<okhttp3.Request.Builder> superCall
|
||||||
) throws Exception {
|
) throws Exception {
|
||||||
|
|
||||||
// print调试
|
|
||||||
log.debug("[intercept] name = {}, value = {}", name, value);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 获取 headers map
|
// 获取上下文
|
||||||
|
|
||||||
|
|
||||||
RequestContext ctx = new RequestContext(builder);
|
RequestContext ctx = new RequestContext(builder);
|
||||||
|
|
||||||
String[] modified = new String[]{value};
|
String[] modified = new String[]{value};
|
||||||
|
|||||||
@@ -4,6 +4,11 @@ import java.util.*;
|
|||||||
import java.util.function.*;
|
import java.util.function.*;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import quant.rich.emoney.patch.okhttp.PatchOkHttp.HeaderInterceptor;
|
||||||
|
|
||||||
public class PatchOkHttpRule {
|
public class PatchOkHttpRule {
|
||||||
private final Predicate<RequestContext> condition;
|
private final Predicate<RequestContext> condition;
|
||||||
private final List<HeaderAction> actions;
|
private final List<HeaderAction> actions;
|
||||||
@@ -37,6 +42,7 @@ public class PatchOkHttpRule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
|
public static final Logger log = LoggerFactory.getLogger(HeaderInterceptor.class);
|
||||||
private Predicate<RequestContext> condition;
|
private Predicate<RequestContext> condition;
|
||||||
private final List<HeaderAction> actions = new ArrayList<>();
|
private final List<HeaderAction> actions = new ArrayList<>();
|
||||||
|
|
||||||
@@ -52,6 +58,10 @@ public class PatchOkHttpRule {
|
|||||||
return and(ctx -> ctx.headers.containsKey(name));
|
return and(ctx -> ctx.headers.containsKey(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder hasNotHeaderName(String name) {
|
||||||
|
return not(r -> r.and(ctx -> ctx.headers.containsKey(name)));
|
||||||
|
}
|
||||||
|
|
||||||
public Builder hasHeaderValueMatch(String name, String regex) {
|
public Builder hasHeaderValueMatch(String name, String regex) {
|
||||||
Pattern pattern = Pattern.compile(regex);
|
Pattern pattern = Pattern.compile(regex);
|
||||||
return and(ctx -> {
|
return and(ctx -> {
|
||||||
@@ -138,6 +148,8 @@ public class PatchOkHttpRule {
|
|||||||
public Builder overrideIf(String headerName, String value) {
|
public Builder overrideIf(String headerName, String value) {
|
||||||
actions.add((ctx, curr, setter) -> {
|
actions.add((ctx, curr, setter) -> {
|
||||||
if (curr.equalsIgnoreCase(headerName)) {
|
if (curr.equalsIgnoreCase(headerName)) {
|
||||||
|
log.debug("matches and applying - host: {}, currHeader {}, targetHeader {}, value: {}, classLoader: {}", ctx.host, curr, headerName,
|
||||||
|
value, this.getClass().getClassLoader());
|
||||||
setter.accept(value);
|
setter.accept(value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import jakarta.validation.ConstraintValidator;
|
|||||||
import jakarta.validation.ConstraintValidatorContext;
|
import jakarta.validation.ConstraintValidatorContext;
|
||||||
import quant.rich.emoney.entity.config.ProxyConfig;
|
import quant.rich.emoney.entity.config.ProxyConfig;
|
||||||
import quant.rich.emoney.interfaces.IConfig;
|
import quant.rich.emoney.interfaces.IConfig;
|
||||||
|
import quant.rich.emoney.pojo.dto.IpInfo;
|
||||||
|
|
||||||
public class ProxyConfigValidator implements IValidator, ConstraintValidator<ProxyConfigValid, IConfig<ProxyConfig>> {
|
public class ProxyConfigValidator implements IValidator, ConstraintValidator<ProxyConfigValid, IConfig<ProxyConfig>> {
|
||||||
|
|
||||||
@@ -25,11 +26,8 @@ public class ProxyConfigValidator implements IValidator, ConstraintValidator<Pro
|
|||||||
return invalid(context, "端口不合法");
|
return invalid(context, "端口不合法");
|
||||||
}
|
}
|
||||||
// 非匿名须判断用户名密码是否为空
|
// 非匿名须判断用户名密码是否为空
|
||||||
boolean valid = ProxyConfig.isProxyEffective(
|
IpInfo ipInfo = config.refreshIpThroughProxy();
|
||||||
config.getProxy(), config.getIgnoreHttpsVerification());
|
return !ipInfo.isEmpty();
|
||||||
if (!valid) {
|
|
||||||
return invalid(context, "代理连接失败,请检查");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
logging.level:
|
logging.level:
|
||||||
'[quant.rich]': debug
|
'[quant.rich]': debug
|
||||||
'[org.springframework.security]': info
|
'[org.springframework.security]': info
|
||||||
|
'[org.springframework.boot.devtools.restart]': debug
|
||||||
|
|
||||||
spring:
|
spring:
|
||||||
cache:
|
cache:
|
||||||
@@ -17,7 +18,9 @@ spring:
|
|||||||
use-key-prefix: true
|
use-key-prefix: true
|
||||||
devtools:
|
devtools:
|
||||||
restart:
|
restart:
|
||||||
|
enabled: true
|
||||||
additional-exclude: '**/*.html'
|
additional-exclude: '**/*.html'
|
||||||
|
additional-paths: lib/
|
||||||
jackson:
|
jackson:
|
||||||
date-format: yyyy-MM-dd HH:mm:ss
|
date-format: yyyy-MM-dd HH:mm:ss
|
||||||
time-zone: Asia/Shanghai
|
time-zone: Asia/Shanghai
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ body {
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-shadow: 0 0px 20px 0 rgb(158 158 158 / 86%);
|
/*box-shadow: 0 0px 20px 0 rgb(158 158 158 / 86%);*/
|
||||||
}
|
}
|
||||||
.main-nav+* {
|
.main-nav+* {
|
||||||
padding-top: 80px;
|
padding-top: 80px;
|
||||||
@@ -19,6 +19,38 @@ body {
|
|||||||
.layui-nav .layui-badge{
|
.layui-nav .layui-badge{
|
||||||
position: relative
|
position: relative
|
||||||
}
|
}
|
||||||
|
.nav-user-info>.layui-nav-img {
|
||||||
|
transition: ease-in-out .4s;
|
||||||
|
z-index: 1;
|
||||||
|
position: relative;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
.nav-user-info:hover>.layui-nav-img {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.nav-user-info::before {
|
||||||
|
transition: ease-in-out .4s;
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 16px;
|
||||||
|
width: 30px; height: 30px;
|
||||||
|
border-radius: 50%;
|
||||||
|
filter: blur(6px);
|
||||||
|
background: conic-gradient(
|
||||||
|
from 0deg,
|
||||||
|
#ff0080, #ff8c00, #ffff00, #00ff80,
|
||||||
|
#00f6ff, #8000ff, #ff0080
|
||||||
|
);
|
||||||
|
z-index: 0;
|
||||||
|
animation: spin 4s linear infinite;
|
||||||
|
}
|
||||||
|
.nav-user-info:hover::before {
|
||||||
|
filter: blur(10px) brightness(1.5);
|
||||||
|
}
|
||||||
|
@keyframes spin {
|
||||||
|
0% { transform: rotate(0deg); }
|
||||||
|
100% { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
.manage-body {
|
.manage-body {
|
||||||
padding: 80px 18% 0
|
padding: 80px 18% 0
|
||||||
}
|
}
|
||||||
@@ -333,7 +365,13 @@ ul.start-list span {
|
|||||||
border: none;
|
border: none;
|
||||||
padding: 0
|
padding: 0
|
||||||
}
|
}
|
||||||
|
.layui-nav {
|
||||||
|
background-color: rgb(241 241 241 / 60%);
|
||||||
|
backdrop-filter: blur(4.2px);
|
||||||
|
}
|
||||||
|
.layui-nav .layui-nav-item a {
|
||||||
|
color: black !important;
|
||||||
|
}
|
||||||
/* 覆盖样式:blockquote */
|
/* 覆盖样式:blockquote */
|
||||||
blockquote.layui-elem-quote {
|
blockquote.layui-elem-quote {
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
@@ -396,3 +434,12 @@ blockquote.layui-elem-quote {
|
|||||||
.bottom-anchor-before.submit-fixed {
|
.bottom-anchor-before.submit-fixed {
|
||||||
height: 54px
|
height: 54px
|
||||||
}
|
}
|
||||||
|
fieldset>legend {
|
||||||
|
font-weight: 500
|
||||||
|
}
|
||||||
|
fieldset>legend:before {
|
||||||
|
content: '#';
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: .25em;
|
||||||
|
color: rgb(22, 183, 119)
|
||||||
|
}
|
||||||
@@ -13,24 +13,7 @@
|
|||||||
益盟操盘手请求头配置,请小心更改。部分参数需要通过模拟的 APP 版本抓包确认。
|
益盟操盘手请求头配置,请小心更改。部分参数需要通过模拟的 APP 版本抓包确认。
|
||||||
</blockquote>
|
</blockquote>
|
||||||
<div class="layui-form" id="editCommentForm" lay-filter="editCommentForm">
|
<div class="layui-form" id="editCommentForm" lay-filter="editCommentForm">
|
||||||
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 20px;">
|
<fieldset class="layui-elem-field layui-field-title">
|
||||||
<legend>代理配置</legend>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="layui-form-item">
|
|
||||||
<label class="layui-form-label">单行选择框</label>
|
|
||||||
<div class="layui-input-block">
|
|
||||||
<select name="proxyType" lay-filter="proxyType">
|
|
||||||
<option value=""></option>
|
|
||||||
<option value="DIRECT">无</option>
|
|
||||||
<option value="HTTP" selected>阅读</option>
|
|
||||||
<option value="SOCKET">游戏</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 20px;">
|
|
||||||
<legend>鉴权配置</legend>
|
<legend>鉴权配置</legend>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
@@ -67,7 +50,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 20px;">
|
<fieldset class="layui-elem-field layui-field-title">
|
||||||
<legend>应用配置</legend>
|
<legend>应用配置</legend>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
@@ -79,7 +62,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">Emoney Version<span>*</span></label>
|
<label class="layui-form-label">应用版本号<span>*</span></label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input class="layui-input" th:value="${@emoneyRequestConfig.emoneyVersion}" name="emoneyVersion" lay-verify="required"/>
|
<input class="layui-input" th:value="${@emoneyRequestConfig.emoneyVersion}" name="emoneyVersion" lay-verify="required"/>
|
||||||
<div class="layui-form-mid layui-word-aux">默认 5.8.1
|
<div class="layui-form-mid layui-word-aux">默认 5.8.1
|
||||||
@@ -95,7 +78,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 20px;">
|
<fieldset class="layui-elem-field layui-field-title">
|
||||||
<legend>设备配置</legend>
|
<legend>设备配置</legend>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
@@ -113,7 +96,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">Device Name<span>*</span></label>
|
<label class="layui-form-label">设备名称<span>*</span></label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input class="layui-input" th:value="${@emoneyRequestConfig.deviceName}" name="deviceName" lay-verify="required"/>
|
<input class="layui-input" th:value="${@emoneyRequestConfig.deviceName}" name="deviceName" lay-verify="required"/>
|
||||||
<div class="layui-form-mid layui-word-aux">对应 build.prop 中 Build.MODEL,用于
|
<div class="layui-form-mid layui-word-aux">对应 build.prop 中 Build.MODEL,用于
|
||||||
@@ -126,7 +109,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">Fingerprint<span>*</span></label>
|
<label class="layui-form-label">指纹<span>*</span></label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input class="layui-input" th:value="${@emoneyRequestConfig.fingerprint}" name="fingerprint" lay-verify="required"/>
|
<input class="layui-input" th:value="${@emoneyRequestConfig.fingerprint}" name="fingerprint" lay-verify="required"/>
|
||||||
<div class="layui-form-mid layui-word-aux">对应 build.prop 中 Build.FINGERPRINT,用于
|
<div class="layui-form-mid layui-word-aux">对应 build.prop 中 Build.FINGERPRINT,用于
|
||||||
@@ -139,19 +122,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">Software Type<span>*</span></label>
|
<label class="layui-form-label">软件类型<span>*</span></label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input class="layui-input" th:value="${@emoneyRequestConfig.softwareType}" name="softwareType" lay-verify="required"/>
|
<input class="layui-input" th:value="${@emoneyRequestConfig.softwareType}" name="softwareType" lay-verify="required"/>
|
||||||
<div class="layui-form-mid layui-word-aux">用于
|
<div class="layui-form-mid layui-word-aux">用于
|
||||||
<ul>
|
<ul>
|
||||||
<li>益盟登录接口 <code><i>softwareType</i> = <b>softwareType</b></code></li>
|
<li>益盟登录接口 <code><i>softwareType</i> = <b>softwareType</b></code></li>
|
||||||
</ul>
|
</ul>
|
||||||
一般和设备有关,需要更改请<a href="javascript:refreshDeviceInfo()">刷新</a>
|
Mobile 或 Pad,一般和设备有关,需要更改请<a href="javascript:refreshDeviceInfo()">刷新</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">Chrome Version<span>*</span></label>
|
<label class="layui-form-label">Chrome<br>版本号<span>*</span></label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input class="layui-input" th:value="${@emoneyRequestConfig.chromeVersion}" name="chromeVersion" lay-verify="required"/>
|
<input class="layui-input" th:value="${@emoneyRequestConfig.chromeVersion}" name="chromeVersion" lay-verify="required"/>
|
||||||
<div class="layui-form-mid layui-word-aux">用于 WebView User-Agent,需要更改请<a href="javascript:refreshChromeVersion()">刷新</a>
|
<div class="layui-form-mid layui-word-aux">用于 WebView User-Agent,需要更改请<a href="javascript:refreshChromeVersion()">刷新</a>
|
||||||
|
|||||||
@@ -27,17 +27,13 @@
|
|||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input class="layui-input" autocomplete="off"
|
<input class="layui-input" autocomplete="off"
|
||||||
th:value="${@proxyConfig.proxyHost}" name="proxyHost"/>
|
th:value="${@proxyConfig.proxyHost}" name="proxyHost"/>
|
||||||
<div class="layui-form-mid layui-word-aux">代理地址
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">代理端口</label>
|
<label class="layui-form-label">代理端口</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input class="layui-input" autocomplete="off" type="number" min="0" max="65535"
|
<input class="layui-input" autocomplete="off" type="number" min="1" max="65535"
|
||||||
th:value="${@proxyConfig.proxyPort}" name="proxyPort" lay-verify="number|ge:0|le:65535"/>
|
th:value="${@proxyConfig.proxyPort}" name="proxyPort" lay-verify="number|gt:0|le:65535"/>
|
||||||
<div class="layui-form-mid layui-word-aux">非匿名登录时的密码
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -50,10 +46,13 @@
|
|||||||
th:checked="${@proxyConfig.ignoreHttpsVerification}"
|
th:checked="${@proxyConfig.ignoreHttpsVerification}"
|
||||||
name="ignoreHttpsVerification" lay-verify="required"/>
|
name="ignoreHttpsVerification" lay-verify="required"/>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="layui-form-mid layui-word-aux">
|
||||||
|
出于安全考虑,当且仅当使用 Fiddler/BurpSuite 调试时才建议启用
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bottom-anchor-before"></div>
|
<div class="bottom-anchor-before"></div>
|
||||||
<div class="layui-form-item submit-toolbar submit-fixed">
|
<div class="layui-form-item submit-toolbar submit-fixed" style="display: none">
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<button class="layui-btn" lay-submit lay-filter="submit">提交</button>
|
<button class="layui-btn" lay-submit lay-filter="submit">提交</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -72,6 +71,9 @@
|
|||||||
|
|
||||||
const observer = new IntersectionObserver(
|
const observer = new IntersectionObserver(
|
||||||
([entry]) => {
|
([entry]) => {
|
||||||
|
if (toolbar.style.display == 'none') {
|
||||||
|
toolbar.style.display = '';
|
||||||
|
}
|
||||||
if (entry.isIntersecting) {
|
if (entry.isIntersecting) {
|
||||||
toolbar.classList.remove('submit-fixed');
|
toolbar.classList.remove('submit-fixed');
|
||||||
toolbar.classList.add('submit-static');
|
toolbar.classList.add('submit-static');
|
||||||
@@ -103,7 +105,7 @@
|
|||||||
document.querySelectorAll('input[lay-skin="switch"]').forEach(checkbox => {
|
document.querySelectorAll('input[lay-skin="switch"]').forEach(checkbox => {
|
||||||
field[checkbox.getAttribute('name')] = checkbox.getAttribute('value') == 'true'
|
field[checkbox.getAttribute('name')] = checkbox.getAttribute('value') == 'true'
|
||||||
});
|
});
|
||||||
|
const load = layui.layer.load(2);
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: location.href,
|
url: location.href,
|
||||||
data: JSON.stringify(field),
|
data: JSON.stringify(field),
|
||||||
@@ -119,6 +121,9 @@
|
|||||||
Dog.error({
|
Dog.error({
|
||||||
msg: r && r.data,
|
msg: r && r.data,
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
complete: function () {
|
||||||
|
layer.close(load);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -54,8 +54,8 @@
|
|||||||
</dl>
|
</dl>
|
||||||
</li>
|
</li>
|
||||||
<li class="layui-nav-item" style="float:right;margin-right: 1px;" lay-unselect="">
|
<li class="layui-nav-item" style="float:right;margin-right: 1px;" lay-unselect="">
|
||||||
<a href="javascript:;" class="nav-last">
|
<a href="javascript:;" class="nav-last nav-user-info" >
|
||||||
<img id="adminUserGravatar" th:src="@{/img/emograb_avatar.png}" loading="lazy" referrerpolicy="same-origin" class="layui-nav-img" />
|
<img id="adminUserGravatar" th:src="@{/img/dog-avatar.webp}" loading="lazy" referrerpolicy="same-origin" class="layui-nav-img" />
|
||||||
<span id="adminUserNickname">[[${@platformConfig.username}]]</span>
|
<span id="adminUserNickname">[[${@platformConfig.username}]]</span>
|
||||||
</a>
|
</a>
|
||||||
<dl class="layui-nav-child">
|
<dl class="layui-nav-child">
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
<input type="hidden" name="id"/>
|
<input type="hidden" name="id"/>
|
||||||
<label class="layui-form-label">用户昵称<span>*</span></label>
|
<label class="layui-form-label">用户昵称<span>*</span></label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input type="text" lay-verify="required" name="nickname" placeholder="" autocomplete="off" class="layui-input"/>
|
<input type="text" lay-verify="required" name="username" placeholder="" autocomplete="off" class="layui-input"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
@@ -79,12 +79,6 @@
|
|||||||
<input type="text" lay-verify="required" name="email" placeholder="" autocomplete="off" class="layui-input"/>
|
<input type="text" lay-verify="required" name="email" placeholder="" autocomplete="off" class="layui-input"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
|
||||||
<label class="layui-form-label">网站</label>
|
|
||||||
<div class="layui-input-block">
|
|
||||||
<input type="text" name="site" placeholder="" autocomplete="off" class="layui-input"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="layui-form-item" id="old-pass">
|
<div class="layui-form-item" id="old-pass">
|
||||||
<label class="layui-form-label">旧密码</label>
|
<label class="layui-form-label">旧密码</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
@@ -115,6 +109,37 @@
|
|||||||
<script th:src="@{/admin/v1/static/js/dog.js}"></script>
|
<script th:src="@{/admin/v1/static/js/dog.js}"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
let refreshTimer = null;
|
||||||
|
|
||||||
|
async function refreshIpThroughProxy() {
|
||||||
|
try {
|
||||||
|
let res = await (await fetch('/admin/v1/config/proxy/refreshIpThroughProxy')).json();
|
||||||
|
if (res.ok) {
|
||||||
|
let ip = res.data.string || '获取代理 IP 失败';
|
||||||
|
document.getElementById('ipThroughProxy').textContent = ip;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('刷新失败:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startAutoRefreshIp() {
|
||||||
|
if (refreshTimer) return; // 避免重复计时
|
||||||
|
const loop = async () => {
|
||||||
|
await refreshIpThroughProxy();
|
||||||
|
refreshTimer = setTimeout(loop, 20000);
|
||||||
|
};
|
||||||
|
loop();
|
||||||
|
}
|
||||||
|
|
||||||
|
function manualRefreshIp() {
|
||||||
|
refreshIpThroughProxy(); // 不再触发 setTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始启动定时刷新
|
||||||
|
startAutoRefreshIp();
|
||||||
|
|
||||||
layui.use(function () {
|
layui.use(function () {
|
||||||
var element = layui.element, form = layui.form, layer = layui.layer;
|
var element = layui.element, form = layui.form, layer = layui.layer;
|
||||||
element.on('nav(demo)', function (elem) {
|
element.on('nav(demo)', function (elem) {
|
||||||
@@ -124,7 +149,7 @@
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/admin/v1/getCurrentUserInfo',
|
url: '/admin/v1/getUserInfo',
|
||||||
success: function (r) {
|
success: function (r) {
|
||||||
layui.layer.open({
|
layui.layer.open({
|
||||||
type: 1,
|
type: 1,
|
||||||
@@ -137,9 +162,8 @@
|
|||||||
content: $('#editUser').html(),
|
content: $('#editUser').html(),
|
||||||
success: function (layero, layerIndex) {
|
success: function (layero, layerIndex) {
|
||||||
var el = $(layero), o = el.find('#old-pass'), n = el.find('#new-pass');
|
var el = $(layero), o = el.find('#old-pass'), n = el.find('#new-pass');
|
||||||
el.find('[name="nickname"]').val(r.data.nickname);
|
el.find('[name="username"]').val(r.data.username);
|
||||||
el.find('[name="email"]').val(r.data.email);
|
el.find('[name="email"]').val(r.data.email);
|
||||||
el.find('[name="site"]').val(r.data.site);
|
|
||||||
el.on('input propertychange', '#new-pass input', function () {
|
el.on('input propertychange', '#new-pass input', function () {
|
||||||
var that = $(this);
|
var that = $(this);
|
||||||
if (that.val()) {
|
if (that.val()) {
|
||||||
@@ -163,7 +187,7 @@
|
|||||||
data.field['newPassword'] = sha3_224(data.field.newPassword);
|
data.field['newPassword'] = sha3_224(data.field.newPassword);
|
||||||
}
|
}
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/admin/v1/setCurrentUserInfo',
|
url: '/admin/v1/changeUserInfo',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: data.field,
|
data: data.field,
|
||||||
success: function (r) {
|
success: function (r) {
|
||||||
@@ -173,8 +197,8 @@
|
|||||||
icon: 1,
|
icon: 1,
|
||||||
time: 1000
|
time: 1000
|
||||||
});
|
});
|
||||||
$('#adminUserNickname').text(r.data.nickname);
|
$('#adminUserNickname').text(r.data.username);
|
||||||
$('#adminUserGravatar').attr('src', r.data.avatarUrl);
|
//$('#adminUserGravatar').attr('src', r.data.avatarUrl);
|
||||||
},
|
},
|
||||||
error: function (res) {
|
error: function (res) {
|
||||||
var r = res.responseJSON;
|
var r = res.responseJSON;
|
||||||
@@ -197,6 +221,10 @@
|
|||||||
</script>
|
</script>
|
||||||
</th:block>
|
</th:block>
|
||||||
<div th:fragment="feet" class="layui-trans layadmin-user-login-footer">
|
<div th:fragment="feet" class="layui-trans layadmin-user-login-footer">
|
||||||
|
<a id="ipThroughProxy" href="javascript:manualRefreshIp()" title="立即刷新">
|
||||||
|
[[${@proxyConfig.ipInfo == null ? '当前 IP 信息:加载中...' :
|
||||||
|
@proxyConfig.ipInfo.string}]]
|
||||||
|
</a><br>
|
||||||
Driven by Latte<br />
|
Driven by Latte<br />
|
||||||
©2025-[[${#dates.format(new java.util.Date().getTime(),'yyyy')}]]
|
©2025-[[${#dates.format(new java.util.Date().getTime(),'yyyy')}]]
|
||||||
<a href="#">Latte</a>
|
<a href="#">Latte</a>
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ package quant.rich;
|
|||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import quant.rich.emoney.patch.okhttp.RequestContext;
|
import quant.rich.emoney.patch.okhttp.RequestContext;
|
||||||
|
import quant.rich.emoney.patch.okhttp.PatchOkHttp;
|
||||||
import quant.rich.emoney.patch.okhttp.PatchOkHttpRule;
|
import quant.rich.emoney.patch.okhttp.PatchOkHttpRule;
|
||||||
|
|
||||||
public class PatchOkHttpTest {
|
public class PatchOkHttpTest {
|
||||||
@@ -22,18 +24,33 @@ public class PatchOkHttpTest {
|
|||||||
|
|
||||||
Assertions.assertTrue(rule.matches(context), "测试失败");
|
Assertions.assertTrue(rule.matches(context), "测试失败");
|
||||||
|
|
||||||
rule = PatchOkHttpRule.when().hostEndsWith("emoney.com")
|
rule = PatchOkHttpRule.when().hostEndsWith("emoney.cn")
|
||||||
.or(r -> r.hostContains("emapp"))
|
.or(c -> c.hostContains("localhost"))
|
||||||
|
.or(a -> a.hostContains("emapp"))
|
||||||
|
.or(b -> b.hasHeaderName("X-Protocol-Id"))
|
||||||
|
.overrideIf("User-Agent", "okhttp/3.12.2")
|
||||||
.build();
|
.build();
|
||||||
context = new RequestContext(Map.of(), "https", "mbs.emoney.com");
|
context = new RequestContext(Map.of(), "https", "mbs.emoney.cn");
|
||||||
|
|
||||||
Assertions.assertTrue(rule.matches(context), "测试失败");
|
Assertions.assertTrue(rule.matches(context), "测试失败");
|
||||||
|
|
||||||
context = new RequestContext(Map.of(), "https", "emapp-static.oss-cn-shanghai.aliyuncs.com");
|
context = new RequestContext(Map.of(), "https", "emapp-static.oss-cn-shanghai.aliyuncs.com");
|
||||||
Assertions.assertTrue(rule.matches(context), "测试失败");
|
Assertions.assertTrue(rule.matches(context), "测试失败");
|
||||||
|
|
||||||
|
// 测试 Override
|
||||||
|
String[] modifier = new String[] {""};
|
||||||
|
Consumer<String> consumer = str -> {
|
||||||
|
modifier[0] = str;
|
||||||
|
};
|
||||||
|
PatchOkHttp.apply(rule);
|
||||||
|
PatchOkHttp.match(context, "User-Agent", consumer);
|
||||||
|
Assertions.assertTrue("okhttp/3.12.2".equals(modifier[0]), "测试失败:User-Agent 覆写失败");
|
||||||
|
|
||||||
|
modifier[0] = "";
|
||||||
context = new RequestContext(Map.of(), "https", "hao123.com");
|
context = new RequestContext(Map.of(), "https", "hao123.com");
|
||||||
Assertions.assertFalse(rule.matches(context), "测试失败");
|
Assertions.assertFalse(rule.matches(context), "测试失败");
|
||||||
|
PatchOkHttp.match(context, "User-Agent", consumer);
|
||||||
|
Assertions.assertTrue("".equals(modifier[0]), "测试失败:User-Agent 不覆写失败");
|
||||||
|
|
||||||
|
|
||||||
//Assertions.assertEquals("{\"info\":\"m\",\"weight\":\"100kg/m\"}", JSON.toJSONString(humanWeight));
|
//Assertions.assertEquals("{\"info\":\"m\",\"weight\":\"100kg/m\"}", JSON.toJSONString(humanWeight));
|
||||||
|
|||||||
Reference in New Issue
Block a user