From 6a5be20d828d54acd23e96d849af1de254ff670c Mon Sep 17 00:00:00 2001 From: shiyi Date: Tue, 22 Apr 2025 17:12:55 +0800 Subject: [PATCH] =?UTF-8?q?0.0.3:=20=E5=A2=9E=E5=8A=A0https=20ssl=E7=99=BD?= =?UTF-8?q?=E5=90=8D=E5=8D=95=EF=BC=8C=E8=B7=B3=E8=BF=87=E8=AE=A4=E8=AF=81?= =?UTF-8?q?=EF=BC=9B=E5=A2=9E=E5=8A=A0=E6=9F=A5=E8=AF=A2=E9=AB=98=E5=BA=A6?= =?UTF-8?q?=E5=B1=82=E7=9A=84=E6=8E=A5=E5=8F=A3=EF=BC=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tablestore-grid-master/pom.xml | 3 + weather-service/pom.xml | 10 ++- .../griddata/operation/GfsDataImport.java | 1 - .../htfp/weather/utils/HttpClientUtils.java | 41 +++++++++- .../weather/utils/ssl/HostInterceptor.java | 22 ++++++ .../utils/ssl/SelectiveHostnameVerifier.java | 31 ++++++++ .../utils/ssl/SelectiveTrustManager.java | 79 +++++++++++++++++++ .../web/controller/ConfigController.java | 4 +- .../controller/SurfaceWeatherController.java | 4 +- .../controller/UpperWeatherController.java | 8 +- .../response/LevelsAvailableResponse.java | 18 +++++ ...rviceImpl.java => GfsDataServiceImpl.java} | 6 +- .../web/service/IUpperDataService.java | 4 +- .../src/main/resources/application.yml | 4 + 14 files changed, 217 insertions(+), 18 deletions(-) create mode 100644 weather-service/src/main/java/com/htfp/weather/utils/ssl/HostInterceptor.java create mode 100644 weather-service/src/main/java/com/htfp/weather/utils/ssl/SelectiveHostnameVerifier.java create mode 100644 weather-service/src/main/java/com/htfp/weather/utils/ssl/SelectiveTrustManager.java create mode 100644 weather-service/src/main/java/com/htfp/weather/web/param/response/LevelsAvailableResponse.java rename weather-service/src/main/java/com/htfp/weather/web/service/{GfsUpperDataServiceImpl.java => GfsDataServiceImpl.java} (99%) diff --git a/tablestore-grid-master/pom.xml b/tablestore-grid-master/pom.xml index 5517961..985663d 100644 --- a/tablestore-grid-master/pom.xml +++ b/tablestore-grid-master/pom.xml @@ -134,6 +134,9 @@ org.springframework.boot spring-boot-maven-plugin + + true + diff --git a/weather-service/pom.xml b/weather-service/pom.xml index 9fccfee..5ab3258 100644 --- a/weather-service/pom.xml +++ b/weather-service/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.htfp weather-service - 0.0.1 + 0.0.3 weather-service weather-service jar @@ -146,6 +146,14 @@ com.htfp.weather.WeatherServiceApplication ZIP + + + + nothing + nothing + + + diff --git a/weather-service/src/main/java/com/htfp/weather/griddata/operation/GfsDataImport.java b/weather-service/src/main/java/com/htfp/weather/griddata/operation/GfsDataImport.java index e60827f..072a6b6 100644 --- a/weather-service/src/main/java/com/htfp/weather/griddata/operation/GfsDataImport.java +++ b/weather-service/src/main/java/com/htfp/weather/griddata/operation/GfsDataImport.java @@ -20,7 +20,6 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; -import sun.nio.ch.DirectBuffer; import ucar.ma2.*; import ucar.nc2.NetcdfFile; import ucar.nc2.NetcdfFiles; diff --git a/weather-service/src/main/java/com/htfp/weather/utils/HttpClientUtils.java b/weather-service/src/main/java/com/htfp/weather/utils/HttpClientUtils.java index b54d497..efeb997 100644 --- a/weather-service/src/main/java/com/htfp/weather/utils/HttpClientUtils.java +++ b/weather-service/src/main/java/com/htfp/weather/utils/HttpClientUtils.java @@ -1,5 +1,8 @@ package com.htfp.weather.utils; +import com.htfp.weather.utils.ssl.HostInterceptor; +import com.htfp.weather.utils.ssl.SelectiveHostnameVerifier; +import com.htfp.weather.utils.ssl.SelectiveTrustManager; import lombok.extern.slf4j.Slf4j; import okhttp3.OkHttpClient; import okhttp3.Request; @@ -16,9 +19,17 @@ import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.file.Files; import java.nio.file.StandardCopyOption; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; +import javax.net.ssl.*; +import javax.net.ssl.SSLContext; +import java.security.SecureRandom; + /** * @Author : shiyi * @Date : 2024/1/23 16:19 @@ -26,13 +37,36 @@ import java.util.concurrent.TimeUnit; */ @Slf4j public class HttpClientUtils { + private static final List TRUSTED_DOMAINS = Arrays.asList("nomads.ncep.noaa.gov"); private static final OkHttpClient OKHTTP_CLIENT = new OkHttpClient.Builder() .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(60, TimeUnit.SECONDS) .writeTimeout(60, TimeUnit.SECONDS) .build(); - private static final OkHttpClient DONLOAD_OKHTTP_CLIENT = new OkHttpClient.Builder() - .build(); + private static final OkHttpClient DOWNLOAD_OKHTTP_CLIENT = buildOKHttpClient(TRUSTED_DOMAINS).build(); + + public static OkHttpClient.Builder buildOKHttpClient(List trustedDomains) { + try { + // 1. 创建自定义 TrustManager + SelectiveTrustManager trustManager = new SelectiveTrustManager(trustedDomains); + // 2. 初始化 SSLContext + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, new TrustManager[]{trustManager}, new SecureRandom()); + final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); + OkHttpClient.Builder builder = new OkHttpClient.Builder(); + builder.sslSocketFactory(sslSocketFactory, trustManager); + builder.addInterceptor(new HostInterceptor()); + builder.hostnameVerifier(new SelectiveHostnameVerifier(trustedDomains)); + return builder; + } catch (NoSuchAlgorithmException | KeyManagementException e) { + e.printStackTrace(); + return new OkHttpClient.Builder(); + } catch (Exception e) { + e.printStackTrace(); + return new OkHttpClient.Builder(); + } + } + // public static T sendGet(String url, Map params, Class responseClass) { // MultiValueMap queryParams = new LinkedMultiValueMap<>(); // if (params != null) { @@ -55,6 +89,7 @@ public class HttpClientUtils { String jsonStr = sendGet(url, params); return JSONUtils.json2obj(jsonStr, responseClass); } + public static String sendGet(String url, Map params) throws IOException { StringBuilder stringBuilder = new StringBuilder(); if (!CollectionUtils.isEmpty(params)) { @@ -98,7 +133,7 @@ public class HttpClientUtils { .url(sourceUrl) .build(); - try (Response response = DONLOAD_OKHTTP_CLIENT.newCall(request).execute()) { + try (Response response = DOWNLOAD_OKHTTP_CLIENT.newCall(request).execute()) { if (response.code() == 200) { assert response.body() != null; InputStream stream = response.body().byteStream(); diff --git a/weather-service/src/main/java/com/htfp/weather/utils/ssl/HostInterceptor.java b/weather-service/src/main/java/com/htfp/weather/utils/ssl/HostInterceptor.java new file mode 100644 index 0000000..732702a --- /dev/null +++ b/weather-service/src/main/java/com/htfp/weather/utils/ssl/HostInterceptor.java @@ -0,0 +1,22 @@ +package com.htfp.weather.utils.ssl; + +import okhttp3.Interceptor; +import okhttp3.Response; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; + +public class HostInterceptor implements Interceptor { + private static final ThreadLocal hostnameHolder = new ThreadLocal<>(); + + @NotNull + @Override + public Response intercept(Chain chain) throws IOException { + hostnameHolder.set(chain.request().url().host()); + return chain.proceed(chain.request()); + } + + public static String getCurrentHostname() { + return hostnameHolder.get(); + } +} diff --git a/weather-service/src/main/java/com/htfp/weather/utils/ssl/SelectiveHostnameVerifier.java b/weather-service/src/main/java/com/htfp/weather/utils/ssl/SelectiveHostnameVerifier.java new file mode 100644 index 0000000..8e2eeda --- /dev/null +++ b/weather-service/src/main/java/com/htfp/weather/utils/ssl/SelectiveHostnameVerifier.java @@ -0,0 +1,31 @@ +package com.htfp.weather.utils.ssl; + +import org.springframework.util.CollectionUtils; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLSession; +import java.util.Arrays; +import java.util.List; + +public class SelectiveHostnameVerifier implements HostnameVerifier { + private List trustedDomains; + private final HostnameVerifier defaultVerifier = HttpsURLConnection.getDefaultHostnameVerifier(); + + public SelectiveHostnameVerifier(){} + + public SelectiveHostnameVerifier(List trustedDomains) { + this.trustedDomains = trustedDomains; + } + + @Override + public boolean verify(String hostname, SSLSession session) { + if (!CollectionUtils.isEmpty(trustedDomains) && trustedDomains.contains(hostname)) { + // 白名单域名:跳过主机名验证 + return true; + } else { + // 其他域名:使用默认验证 + return defaultVerifier.verify(hostname, session); + } + } +} diff --git a/weather-service/src/main/java/com/htfp/weather/utils/ssl/SelectiveTrustManager.java b/weather-service/src/main/java/com/htfp/weather/utils/ssl/SelectiveTrustManager.java new file mode 100644 index 0000000..fcd5c1d --- /dev/null +++ b/weather-service/src/main/java/com/htfp/weather/utils/ssl/SelectiveTrustManager.java @@ -0,0 +1,79 @@ +package com.htfp.weather.utils.ssl; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.CollectionUtils; + +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; +import java.security.KeyStore; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.List; + +@Slf4j +public class SelectiveTrustManager implements X509TrustManager { + // 目标域名白名单(如:example.com) + private final List trustedDomains; + private final X509TrustManager defaultTrustManager; + private final X509TrustManager fakeTrustManager; + + public SelectiveTrustManager(List trustedDomains) throws Exception { + this.trustedDomains = trustedDomains; + // 获取默认 TrustManager + TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init((KeyStore) null); + defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0]; + + // 创建一个“信任所有证书”的 TrustManager(仅用于白名单域名) + fakeTrustManager = 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[0]; + } + }; + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) { + // 客户端验证交给默认逻辑 + try { + defaultTrustManager.checkClientTrusted(chain, authType); + + } catch (CertificateException e) { + throw new RuntimeException(e); + } + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + // 获取当前请求的域名 + String hostname = getCurrentHostname(); // 需要额外逻辑获取当前域名 + if (!CollectionUtils.isEmpty(trustedDomains) && trustedDomains.contains(hostname)) { + // 白名单域名:跳过证书验证 + log.debug("{} is in trusted domains, skipping certificate verification", hostname); + fakeTrustManager.checkServerTrusted(chain, authType); + } else { + // 非白名单域名:使用默认验证 + defaultTrustManager.checkServerTrusted(chain, authType); + } + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return defaultTrustManager.getAcceptedIssuers(); + } + + // 需要结合 OkHttp 拦截器或反射获取当前请求域名(见后续优化) + private String getCurrentHostname() { + return HostInterceptor.getCurrentHostname(); + } +} \ No newline at end of file diff --git a/weather-service/src/main/java/com/htfp/weather/web/controller/ConfigController.java b/weather-service/src/main/java/com/htfp/weather/web/controller/ConfigController.java index 8613db5..7fdcbb0 100644 --- a/weather-service/src/main/java/com/htfp/weather/web/controller/ConfigController.java +++ b/weather-service/src/main/java/com/htfp/weather/web/controller/ConfigController.java @@ -17,7 +17,7 @@ import com.htfp.weather.web.param.response.DownloadResult; import com.htfp.weather.web.param.response.ImportResult; import com.htfp.weather.web.param.response.Result; import com.htfp.weather.web.service.FileService; -import com.htfp.weather.web.service.GfsUpperDataServiceImpl; +import com.htfp.weather.web.service.GfsDataServiceImpl; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.springframework.util.CollectionUtils; @@ -52,7 +52,7 @@ public class ConfigController { @Resource FileService fileService; @Resource - GfsUpperDataServiceImpl gfsDataService; + GfsDataServiceImpl gfsDataService; @Resource SurfaceWeatherController surfaceWeatherController; diff --git a/weather-service/src/main/java/com/htfp/weather/web/controller/SurfaceWeatherController.java b/weather-service/src/main/java/com/htfp/weather/web/controller/SurfaceWeatherController.java index e596e0b..3b38e49 100644 --- a/weather-service/src/main/java/com/htfp/weather/web/controller/SurfaceWeatherController.java +++ b/weather-service/src/main/java/com/htfp/weather/web/controller/SurfaceWeatherController.java @@ -8,7 +8,7 @@ import com.htfp.weather.web.param.response.NowWeatherStatus; import com.htfp.weather.web.param.response.Result; import com.htfp.weather.web.param.response.SurfaceWeatherWarning; import com.htfp.weather.web.param.response.TimeSeriesDataset; -import com.htfp.weather.web.service.GfsUpperDataServiceImpl; +import com.htfp.weather.web.service.GfsDataServiceImpl; import com.htfp.weather.web.service.surfaceapi.CaiYunServiceImpl; import com.htfp.weather.web.service.surfaceapi.CmaServiceImpl; import com.htfp.weather.web.service.surfaceapi.HeFengServiceImpl; @@ -38,7 +38,7 @@ public class SurfaceWeatherController { @Resource CaiYunServiceImpl caiYunService; @Resource - GfsUpperDataServiceImpl gfsDataService; + GfsDataServiceImpl gfsDataService; ISurfaceDataService service; @PostConstruct public void init() { diff --git a/weather-service/src/main/java/com/htfp/weather/web/controller/UpperWeatherController.java b/weather-service/src/main/java/com/htfp/weather/web/controller/UpperWeatherController.java index 4849003..9c8388c 100644 --- a/weather-service/src/main/java/com/htfp/weather/web/controller/UpperWeatherController.java +++ b/weather-service/src/main/java/com/htfp/weather/web/controller/UpperWeatherController.java @@ -6,7 +6,7 @@ import com.htfp.weather.web.exception.AppException; import com.htfp.weather.web.exception.ErrorCode; import com.htfp.weather.web.param.request.*; import com.htfp.weather.web.param.response.*; -import com.htfp.weather.web.service.GfsUpperDataServiceImpl; +import com.htfp.weather.web.service.GfsDataServiceImpl; import lombok.extern.slf4j.Slf4j; import org.springframework.util.CollectionUtils; import org.springframework.validation.annotation.Validated; @@ -29,7 +29,7 @@ import java.util.List; public class UpperWeatherController { @Resource(name = "tablestore-gfs") - GfsUpperDataServiceImpl gfsDataService; + GfsDataServiceImpl gfsDataService; @PostMapping("/queryUpperNowWeather") @ControllerLog(info = "高空实时气象信息查询") @@ -50,7 +50,7 @@ public class UpperWeatherController { List longitudeList = new ArrayList<>(size); checkLatitudeLongitude(latitudeList, longitudeList, pointList); int level = multiPointsRequest.getLevel(); - List nowWeatherByMultiPoint = gfsDataService.getNowWeatherByMultiPoint(latitudeList, longitudeList, level); + List nowWeatherByMultiPoint = gfsDataService.getNowWeatherByMultiPoint(level, latitudeList, longitudeList); return Result.success(nowWeatherByMultiPoint); } @@ -73,7 +73,7 @@ public class UpperWeatherController { List longitudeList = new ArrayList<>(size); checkLatitudeLongitude(latitudeList, longitudeList, pointList); int level = multiPointsRequest.getLevel(); - List nowWeatherByMultiPoint = gfsDataService.getForecastSeriesByMultiPoint(latitudeList, longitudeList, level); + List nowWeatherByMultiPoint = gfsDataService.getForecastSeriesByMultiPoint(level, latitudeList, longitudeList); return Result.success(nowWeatherByMultiPoint); } diff --git a/weather-service/src/main/java/com/htfp/weather/web/param/response/LevelsAvailableResponse.java b/weather-service/src/main/java/com/htfp/weather/web/param/response/LevelsAvailableResponse.java new file mode 100644 index 0000000..ad3f4b7 --- /dev/null +++ b/weather-service/src/main/java/com/htfp/weather/web/param/response/LevelsAvailableResponse.java @@ -0,0 +1,18 @@ +package com.htfp.weather.web.param.response; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data @NoArgsConstructor @AllArgsConstructor +public class LevelsAvailableResponse { + List levelsAvailableList; + @Data @NoArgsConstructor @AllArgsConstructor + public static class LevelsAvailable { + String dataSource; + List pressureLevels; + List heightLevels; + } +} diff --git a/weather-service/src/main/java/com/htfp/weather/web/service/GfsUpperDataServiceImpl.java b/weather-service/src/main/java/com/htfp/weather/web/service/GfsDataServiceImpl.java similarity index 99% rename from weather-service/src/main/java/com/htfp/weather/web/service/GfsUpperDataServiceImpl.java rename to weather-service/src/main/java/com/htfp/weather/web/service/GfsDataServiceImpl.java index 4de2c27..3cfe3cd 100644 --- a/weather-service/src/main/java/com/htfp/weather/web/service/GfsUpperDataServiceImpl.java +++ b/weather-service/src/main/java/com/htfp/weather/web/service/GfsDataServiceImpl.java @@ -42,7 +42,7 @@ import java.util.stream.Collectors; */ @Service("tablestore-gfs") @Slf4j -public class GfsUpperDataServiceImpl implements IUpperDataService, ISurfaceDataService { +public class GfsDataServiceImpl implements IUpperDataService, ISurfaceDataService { @Resource GfsDataFetcher gfsDataFetcher; @Resource @@ -163,7 +163,7 @@ public class GfsUpperDataServiceImpl implements IUpperDataService, ISurfaceDataS * @return */ @Override - public List getNowWeatherByMultiPoint(List latitude, List longitude, int level) { + public List getNowWeatherByMultiPoint(int level, List latitude, List longitude) { if (latitude.size() != longitude.size()) { throw new AppException(ErrorCode.INDEX_SIZE_ERROR, ": 经纬度数组大小不一致"); } @@ -233,7 +233,7 @@ public class GfsUpperDataServiceImpl implements IUpperDataService, ISurfaceDataS * @return */ @Override - public List getForecastSeriesByMultiPoint(List latitudeList, List longitudeList, int level) { + public List getForecastSeriesByMultiPoint(int level, List latitudeList, List longitudeList) { if (latitudeList.size() != longitudeList.size()) { throw new AppException(ErrorCode.INDEX_SIZE_ERROR, ": 经纬度数组大小不一致"); } diff --git a/weather-service/src/main/java/com/htfp/weather/web/service/IUpperDataService.java b/weather-service/src/main/java/com/htfp/weather/web/service/IUpperDataService.java index d25c062..0ac3f14 100644 --- a/weather-service/src/main/java/com/htfp/weather/web/service/IUpperDataService.java +++ b/weather-service/src/main/java/com/htfp/weather/web/service/IUpperDataService.java @@ -16,11 +16,11 @@ import java.util.List; public interface IUpperDataService { NowWeatherStatus getNowWeather(int level, double latitude, double longitude); - List getNowWeatherByMultiPoint(List latitude, List longitude, int level); + List getNowWeatherByMultiPoint(int level, List latitude, List longitude); TimeSeriesDataset getForecastSeries(int level, double latitude, double longitude); - List getForecastSeriesByMultiPoint(List latitudeList, List longitudeList, int level); + List getForecastSeriesByMultiPoint(int level, List latitudeList, List longitudeList); ProfileDataset getProfileByPressure(OffsetDateTime targetTime, double latitude, double longitude); diff --git a/weather-service/src/main/resources/application.yml b/weather-service/src/main/resources/application.yml index 266878a..e81e2d4 100644 --- a/weather-service/src/main/resources/application.yml +++ b/weather-service/src/main/resources/application.yml @@ -29,3 +29,7 @@ mail: send: from: shiyi@htsdfp.com to: shiyi@htsdfp.com + +logging: + level: + com.htfp.weather: debug \ No newline at end of file