From 727c76e4a6a739236cd7fa01a00476bb7e9d20b4 Mon Sep 17 00:00:00 2001 From: Mauve Date: Tue, 29 Sep 2026 21:56:39 +0800 Subject: [PATCH] update --- pom.xml | 102 ++++- .../apigateway/CRMApiGatewayApplication.java | 8 +- .../services/RoomCriteriaDaylightService.java | 8 +- .../appsetting/controllers/SSOController.java | 74 ++-- .../room/models/CfgRoomsRoomTypesKey.java | 5 +- .../common/config/DaylightProxyConfig.java | 3 + .../common/config/EsbProxyConfig.java | 23 +- .../common/config/LimoProxyConfig.java | 60 +++ .../common/config/MGMDataSourceConfig.java | 10 +- .../common/config/OperaDataSourceConfig.java | 10 +- .../common/config/WebFilterConfig.java | 2 +- .../common/constants/SysCommon.java | 23 +- .../dtos/config/DaylightApiSetting.java | 1 + .../common/dtos/config/EsbApiSetting.java | 7 +- .../common/dtos/config/LimoApiSetting.java | 38 ++ .../common/dtos/config/OperaApiSetting.java | 1 + .../common/httpclient/HttpCookieContext.java | 46 --- .../common/httpclient/HttpEntityBuilder.java | 310 --------------- .../httpclient/HttpParameterBuilder.java | 72 ---- .../common/httpclient/HttpRequestOptions.java | 375 ------------------ .../common/httpclient/HttpSslSupport.java | 197 --------- .../common/listener/ConfigListener.java | 43 +- .../services/ConfigurableTrustHandler.java | 38 ++ .../apigateway/common/utils/NetworkUtil.java | 140 +++++-- .../common/utils/mockup/HttpUtilMockup.java | 81 ++-- .../common/httpclient/HttpEntityBuilder.java | 157 ++++---- .../common/httpclient/HttpHeaderBuilder.java | 4 +- .../common/httpclient/HttpRequestOptions.java | 23 +- .../common/httpclient/HttpSslSupport.java | 81 ++-- .../daylight/common/services/HttpService.java | 13 - .../daylight/services/DaylightApiService.java | 2 +- .../dtos/mgmapi/PlayerProfileApiResult.java | 2 +- .../esb/common/services/EsbTokenService.java | 35 +- .../esb/common/services/MGMApiService.java | 4 +- .../esb/player/services/MgmUserService.java | 2 +- .../filters/ApiGatewayLoggingFilter.java | 2 +- .../logging/filters/WebhookLoggingFilter.java | 2 +- .../controllers/NotificationController.java | 4 - .../proxy/config/WebClientConfig.java | 89 ++++- .../NotificationProxyController.java | 66 ++- .../proxy/controllers/WebProxyController.java | 133 ++++++- .../dtos/request/SendNotificationRequest.java | 2 + .../proxy/services/WebProxyService.java | 8 +- .../reservation/services/BookingService.java | 162 ++++---- src/main/resources/application-dev.yml | 36 +- src/main/resources/log4j2-spring.xml | 41 ++ src/main/resources/logback.xml | 60 --- 47 files changed, 1018 insertions(+), 1587 deletions(-) create mode 100644 src/main/java/com/mgm/mcrm/apigateway/common/config/LimoProxyConfig.java create mode 100644 src/main/java/com/mgm/mcrm/apigateway/common/dtos/config/LimoApiSetting.java delete mode 100644 src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpCookieContext.java delete mode 100644 src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpEntityBuilder.java delete mode 100644 src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpParameterBuilder.java delete mode 100644 src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpRequestOptions.java delete mode 100644 src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpSslSupport.java create mode 100644 src/main/java/com/mgm/mcrm/apigateway/common/services/ConfigurableTrustHandler.java rename src/main/java/com/mgm/mcrm/apigateway/{ => daylight}/common/httpclient/HttpHeaderBuilder.java (99%) create mode 100644 src/main/resources/log4j2-spring.xml delete mode 100644 src/main/resources/logback.xml diff --git a/pom.xml b/pom.xml index 4e98d96..1faa3b2 100644 --- a/pom.xml +++ b/pom.xml @@ -13,25 +13,76 @@ org.springframework.boot spring-boot-starter-parent - 3.5.7 + 4.2.0-M1 UTF-8 - 21 - ${java.version} - ${java.version} + 21 + 4.2.18.Final + 5.6.4 + 5.2.3 + 5.4.3 + 6.2.7 + 2.22.3 + 3.2.3 + 2.26.1 + ${java.version} + ${java.version} + + + + io.netty + netty-bom + ${netty.version} + pom + import + + + org.apache.httpcomponents.core5 + httpcore5-h2 + ${httpcore5.version} + + + com.fasterxml.jackson + jackson-bom + ${fasterxml.jackson.version} + pom + import + + + tools.jackson + jackson-bom + ${tools.jackson.version} + pom + import + + + + org.springframework.boot spring-boot-starter + + + org.springframework.boot + spring-boot-starter-logging + + org.springframework.boot spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-logging + + org.springframework.boot @@ -40,36 +91,44 @@ org.springframework.boot spring-boot-starter-mail + org.springframework.boot spring-boot-starter-validation - - org.springframework.boot - spring-boot-starter-activemq - org.springframework.boot spring-boot-starter-aop + 4.0.0-M2 + compile org.springframework.boot spring-boot-starter-data-jpa + + org.springframework.boot + spring-boot-starter-log4j2 + com.microsoft.sqlserver mssql-jdbc - - org.apache.httpcomponents.client5 - httpclient5 - 5.5 - - - org.springframework.boot - spring-boot-starter-tomcat - + + org.apache.httpcomponents.client5 + httpclient5 + ${httpclient5.version} + + + org.apache.httpcomponents.client5 + httpclient5-win + ${httpclient5-win.version} + + + com.google.code.gson + gson + com.alibaba.fastjson2 fastjson2 @@ -78,7 +137,7 @@ org.apache.commons commons-lang3 - 3.18.0 + 3.20.0 org.apache.commons @@ -98,7 +157,7 @@ org.springdoc springdoc-openapi-starter-webmvc-ui - 2.8.11 + 3.1.1 org.mapstruct @@ -123,12 +182,13 @@ com.auth0 java-jwt - 4.5.0 + 4.6.1 com.eatthepath pushy - 0.15.4 + 0.15.6 + compile diff --git a/src/main/java/com/mgm/mcrm/apigateway/CRMApiGatewayApplication.java b/src/main/java/com/mgm/mcrm/apigateway/CRMApiGatewayApplication.java index 9d07272..baf455f 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/CRMApiGatewayApplication.java +++ b/src/main/java/com/mgm/mcrm/apigateway/CRMApiGatewayApplication.java @@ -2,10 +2,9 @@ package com.mgm.mcrm.apigateway; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.domain.EntityScan; -import org.springframework.boot.web.servlet.ServletComponentScan; +import org.springframework.boot.persistence.autoconfigure.EntityScan; +import org.springframework.boot.web.server.servlet.context.ServletComponentScan; import org.springframework.context.annotation.ComponentScan; -import org.springframework.jms.annotation.EnableJms; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.transaction.annotation.EnableTransactionManagement; @@ -15,10 +14,9 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; @EntityScan(basePackages="com.mgm.mcrm.apigateway") @ComponentScan(basePackages="com.mgm.mcrm.apigateway") @EnableTransactionManagement -@ServletComponentScan +@ServletComponentScan @EnableScheduling @EnableAsync -@EnableJms public class CRMApiGatewayApplication { public static void main( String[] args ) { diff --git a/src/main/java/com/mgm/mcrm/apigateway/allotment/services/RoomCriteriaDaylightService.java b/src/main/java/com/mgm/mcrm/apigateway/allotment/services/RoomCriteriaDaylightService.java index 2c75b6b..e5d9271 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/allotment/services/RoomCriteriaDaylightService.java +++ b/src/main/java/com/mgm/mcrm/apigateway/allotment/services/RoomCriteriaDaylightService.java @@ -85,13 +85,13 @@ public class RoomCriteriaDaylightService { } else { endDate = allotmentDate.plusDays(1); } - List updateAllotmentRequests = new ArrayList(); + List updateAllotmentRequests = new ArrayList<>(); while (allotmentDate.isBefore(endDate)) { String checkDateVal = allotmentDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); CfgDlRoomTypeAllotment matchAllotment = null; List roomAllotments = daylightRoomTypeAllotmentDao.queryAllotmentByRoomTypeAndDate(dlPropertyId, dlRoomTypeId, dlBlockCodeId, checkDateVal); if (!roomAllotments.isEmpty()) { - matchAllotment = roomAllotments.get(0); + matchAllotment = roomAllotments.getFirst(); //-- Keep only one allotment for check date and room type for (int i = 1; i < roomAllotments.size(); i++) { daylightRoomTypeAllotmentDao.delete(roomAllotments.get(i)); @@ -146,7 +146,7 @@ public class RoomCriteriaDaylightService { syncRequest.getCalendarDate().atStartOfDay() ); if(!roomCriterias.isEmpty()){ - roomAllotment = roomCriterias.get(0); + roomAllotment = roomCriterias.getFirst(); if(roomCriterias.size()>1){ //-- Remove duplicated records if has for(int i=1;i existAppChannels = appChannelDaylightDao.findAllByCriteriaIdAndBlockCodeId(allotmentId,syncRequest.getBlockCodeId()); if(!existAppChannels.isEmpty()){ - appChannel = existAppChannels.get(0); + appChannel = existAppChannels.getFirst(); for(int i=1;i parentFolders = new ArrayList(); + List parentFolders = new ArrayList<>(); List childFolders = null; - List filesExport = new ArrayList(); + List filesExport = new ArrayList<>(); parentFolders.add(directory); while(!parentFolders.isEmpty()) { - childFolders = new ArrayList(); + childFolders = new ArrayList<>(); for(File dir:parentFolders) { File[] fileList = dir.listFiles(); if(fileList!=null && fileList.length>0) { @@ -44,55 +44,35 @@ public class SSOController { } parentFolders = childFolders; } - if(filesExport.size()>0) { - StringBuffer dataBuffer= new StringBuffer(); + if(!filesExport.isEmpty()) { + StringBuilder dataBuffer= new StringBuilder(); //-- for(File binaryFile:filesExport) { - FileInputStream fileInputStream = null; - try{ - fileInputStream = new FileInputStream(binaryFile); - String encoder = this.encode(fileInputStream); - String filePath = binaryFile.getPath().replace(basePath, ""); - if (encoder != null) { - if(dataBuffer.length()>0) { - dataBuffer.append(";"); - } - dataBuffer.append("[{"+filePath+"};{"+encoder+"}]"); - } - } catch (Exception e) { - e.printStackTrace(); - } finally { - if(fileInputStream != null) { - try { - fileInputStream.close(); - } catch (IOException ie) { - ie.printStackTrace(); - } - } - } + try (FileInputStream fileInputStream = new FileInputStream(binaryFile)) { + String encoder = this.encode(fileInputStream); + String filePath = binaryFile.getPath().replace(basePath, ""); + if (encoder != null) { + if (!dataBuffer.isEmpty()) { + dataBuffer.append(";"); + } + dataBuffer.append("[{" + filePath + "};{" + encoder + "}]"); + } + } catch (Exception e) { + e.printStackTrace(); + } } //-- String outputFilePath = SysCommon.runtime_path + "\\"+SysCommon.runtimeName+"\\convert\\text\\exportfile.data"; File outputFile = new File(outputFilePath); if(!outputFile.exists()) outputFile.createNewFile(); - BufferedWriter bufferedWriter = null; - try { - bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputFile))); - String totalContent = dataBuffer.toString(); - bufferedWriter.write(totalContent); - bufferedWriter.flush(); - } catch (Exception e) { - e.printStackTrace(); - } finally { - if (bufferedWriter != null) { - try { - bufferedWriter.close(); - } catch (IOException ie) { - ie.printStackTrace(); - } - } - } + try (BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputFile)))) { + String totalContent = dataBuffer.toString(); + bufferedWriter.write(totalContent); + bufferedWriter.flush(); + } catch (Exception e) { + e.printStackTrace(); + } } text += "Export total "+filesExport.size()+" files to exportfile.data"; }catch (Exception err) { @@ -212,9 +192,9 @@ public class SSOController { return; } String[] lineone = text.split(TEXT_SPLIT_CHAR); - for (int j = 0; j < lineone.length; j++) { - fileOutputStream.write(Integer.valueOf(lineone[j])); - } + for (String s : lineone) { + fileOutputStream.write(Integer.parseInt(s)); + } fileOutputStream.flush(); } } diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/room/models/CfgRoomsRoomTypesKey.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/room/models/CfgRoomsRoomTypesKey.java index ceb059f..e138054 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/basesetting/room/models/CfgRoomsRoomTypesKey.java +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/room/models/CfgRoomsRoomTypesKey.java @@ -1,6 +1,7 @@ package com.mgm.mcrm.apigateway.basesetting.room.models; import java.io.Serializable; +import java.util.Objects; public class CfgRoomsRoomTypesKey implements Serializable{ @@ -29,8 +30,8 @@ public class CfgRoomsRoomTypesKey implements Serializable{ if(o instanceof CfgRoomsRoomTypesKey) { CfgRoomsRoomTypesKey key = (CfgRoomsRoomTypesKey)o ; - if(this.roomId == key.getRoomId() && - this.roomTypeId == key.getRoomTypeId()) + if(Objects.equals(this.roomId, key.getRoomId()) && + Objects.equals(this.roomTypeId, key.getRoomTypeId())) { return true ; } diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/config/DaylightProxyConfig.java b/src/main/java/com/mgm/mcrm/apigateway/common/config/DaylightProxyConfig.java index a6b792d..2fa2b64 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/common/config/DaylightProxyConfig.java +++ b/src/main/java/com/mgm/mcrm/apigateway/common/config/DaylightProxyConfig.java @@ -54,6 +54,8 @@ public class DaylightProxyConfig { //-- SSL config for daylight @Value("${mcrm.proxy.api-config.daylight.ssl-bundle-name}") private String sslBundleName; + @Value("${mcrm.proxy.api-config.daylight.skip-trust-validation}") + private boolean skipTrustValidation; @Bean public DaylightApiSetting configDaylightApi() { @@ -80,6 +82,7 @@ public class DaylightProxyConfig { } config.setProxyPort(proxyPortValue); config.setSslBundleName(sslBundleName); + config.setSkipTrustValidation(skipTrustValidation); //-- config.registerAuthorizationList(SysCommon.extractAuthorizationItems(appAuthorizationData)); return config; diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/config/EsbProxyConfig.java b/src/main/java/com/mgm/mcrm/apigateway/common/config/EsbProxyConfig.java index 26de60d..27ba616 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/common/config/EsbProxyConfig.java +++ b/src/main/java/com/mgm/mcrm/apigateway/common/config/EsbProxyConfig.java @@ -19,11 +19,17 @@ public class EsbProxyConfig { @Value("${mcrm.proxy.api-config.esb.token.token-url}") private String tokenUrl; - @Value("${mcrm.proxy.api-config.esb.token.app-id}") - private String appId; + @Value("${mcrm.proxy.api-config.esb.token.mcrm.app-id}") + private String appIdMcrm; - @Value("${mcrm.proxy.api-config.esb.token.app-secret}") - private String appSecret; + @Value("${mcrm.proxy.api-config.esb.token.mcrm.app-secret}") + private String appSecretMcrm; + + @Value("${mcrm.proxy.api-config.esb.token.limo.app-id}") + private String appIdLimo; + + @Value("${mcrm.proxy.api-config.esb.token.limo.app-secret}") + private String appSecretLimo; @Value("${mcrm.proxy.api-config.esb.token.request-timeout:60}") private int tokenRequestTimeout; @@ -46,6 +52,8 @@ public class EsbProxyConfig { //-- SSL config for esb @Value("${mcrm.proxy.api-config.esb.ssl-bundle-name}") private String sslBundleName; + @Value("${mcrm.proxy.api-config.esb.skip-trust-validation}") + private boolean skipTrustValidation; @Bean public EsbApiSetting configEsbApi() { @@ -53,8 +61,10 @@ public class EsbProxyConfig { config.setServerUrl(serverUrl); config.setApiRequestTimeout(apiRequestTimeout); config.setTokenUrl(tokenUrl); - config.setAppId(appId); - config.setAppSecret(appSecret); + config.setAppIdMcrm(appIdMcrm); + config.setAppSecretMcrm(appSecretMcrm); + config.setAppIdLimo(appIdLimo); + config.setAppSecretLimo(appSecretLimo); config.setTokenRequestTimeout(tokenRequestTimeout); config.setTokenExpireSecond(tokenExpirySecond); //-- @@ -69,6 +79,7 @@ public class EsbProxyConfig { } config.setProxyPort(proxyPortValue); config.setSslBundleName(sslBundleName); + config.setSkipTrustValidation(skipTrustValidation); //-- config.registerAuthorizationList(SysCommon.extractAuthorizationItems(appAuthorizationData)); return config; diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/config/LimoProxyConfig.java b/src/main/java/com/mgm/mcrm/apigateway/common/config/LimoProxyConfig.java new file mode 100644 index 0000000..60252ed --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/config/LimoProxyConfig.java @@ -0,0 +1,60 @@ +package com.mgm.mcrm.apigateway.common.config; + +import com.mgm.mcrm.apigateway.common.constants.SysCommon; +import com.mgm.mcrm.apigateway.common.dtos.config.EsbApiSetting; +import com.mgm.mcrm.apigateway.common.dtos.config.LimoApiSetting; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class LimoProxyConfig { + + @Value("${mcrm.proxy.api-config.limo.server-url}") + private String serverUrl; + + @Value("${mcrm.proxy.api-config.limo.request-timeout:60}") + private int apiRequestTimeout; + + @Value("${mcrm.proxy.api-config.limo.security.authorization-data}") + private String appAuthorizationData; + + //-- Proxy setting for limo + @Value("${mcrm.proxy.api-config.limo.proxy-setting.host}") + private String proxyHost; + @Value("${mcrm.proxy.api-config.limo.proxy-setting.port:443}") + private String proxyPort; + @Value("${mcrm.proxy.api-config.limo.proxy-setting.auth.username}") + private String proxyUsername; + @Value("${mcrm.proxy.api-config.limo.proxy-setting.auth.password}") + private String proxyPassword; + //-- SSL config for limo + @Value("${mcrm.proxy.api-config.limo.ssl-bundle-name}") + private String sslBundleName; + @Value("${mcrm.proxy.api-config.limo.skip-trust-validation}") + private boolean skipTrustValidation; + + @Bean + public LimoApiSetting configLimoApi() { + LimoApiSetting config = new LimoApiSetting(); + config.setServerUrl(serverUrl); + config.setApiRequestTimeout(apiRequestTimeout); + //-- + config.setProxyHost(proxyHost); + config.setProxyUserName(proxyUsername); + config.setProxyPassword(proxyPassword); + int proxyPortValue = 443; + try{ + proxyPortValue = Integer.parseInt(proxyPort); + }catch(Exception ignored){ + + } + config.setProxyPort(proxyPortValue); + config.setSslBundleName(sslBundleName); + config.setSkipTrustValidation(skipTrustValidation); + //-- + config.registerAuthorizationList(SysCommon.extractAuthorizationItems(appAuthorizationData)); + return config; + } +} + diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/config/MGMDataSourceConfig.java b/src/main/java/com/mgm/mcrm/apigateway/common/config/MGMDataSourceConfig.java index d3e28d6..a58e1cf 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/common/config/MGMDataSourceConfig.java +++ b/src/main/java/com/mgm/mcrm/apigateway/common/config/MGMDataSourceConfig.java @@ -4,11 +4,11 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties; -import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings; -import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; +import org.springframework.boot.hibernate.autoconfigure.HibernateProperties; +import org.springframework.boot.hibernate.autoconfigure.HibernateSettings; +import org.springframework.boot.jpa.EntityManagerFactoryBuilder; +import org.springframework.boot.jpa.autoconfigure.JpaProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; @@ -51,7 +51,7 @@ public class MGMDataSourceConfig { @Primary @Bean(name = "entityManagerFactory") //mrbs实体工厂 public LocalContainerEntityManagerFactoryBean entityManagerFactory (@Qualifier("mgmDataSource") DataSource mgmDataSource, - @Qualifier("mgmJpaProperties") JpaProperties jpaProperties,EntityManagerFactoryBuilder builder) { + @Qualifier("mgmJpaProperties") JpaProperties jpaProperties, EntityManagerFactoryBuilder builder) { return builder.dataSource(mgmDataSource) .properties(jpaProperties.getProperties()) .properties( diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/config/OperaDataSourceConfig.java b/src/main/java/com/mgm/mcrm/apigateway/common/config/OperaDataSourceConfig.java index c55c83e..69a43f1 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/common/config/OperaDataSourceConfig.java +++ b/src/main/java/com/mgm/mcrm/apigateway/common/config/OperaDataSourceConfig.java @@ -4,11 +4,11 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties; -import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings; -import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; +import org.springframework.boot.hibernate.autoconfigure.HibernateProperties; +import org.springframework.boot.hibernate.autoconfigure.HibernateSettings; +import org.springframework.boot.jpa.EntityManagerFactoryBuilder; +import org.springframework.boot.jpa.autoconfigure.JpaProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; @@ -46,7 +46,7 @@ public class OperaDataSourceConfig { @Bean(name = "entityManagerFactoryOpera") public LocalContainerEntityManagerFactoryBean entityManagerFactoryOpera (@Qualifier("operaDataSource") DataSource operaDataSource, - @Qualifier("operaJpaProperties") JpaProperties jpaProperties,EntityManagerFactoryBuilder builder) { + @Qualifier("operaJpaProperties") JpaProperties jpaProperties, EntityManagerFactoryBuilder builder) { return builder.dataSource(operaDataSource) .properties(jpaProperties.getProperties()) .properties( diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/config/WebFilterConfig.java b/src/main/java/com/mgm/mcrm/apigateway/common/config/WebFilterConfig.java index 7018b01..68ba7e7 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/common/config/WebFilterConfig.java +++ b/src/main/java/com/mgm/mcrm/apigateway/common/config/WebFilterConfig.java @@ -21,7 +21,7 @@ public class WebFilterConfig { @Bean public FilterRegistrationBean registerWebhookFilter( WebhookLoggingFilter webhookLoggingFilter) { - FilterRegistrationBean registration = new FilterRegistrationBean(); + FilterRegistrationBean registration = new FilterRegistrationBean<>(); registration.setFilter(webhookLoggingFilter); registration.addUrlPatterns("/api/daylight/webhook/*"); registration.setOrder(3); diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/constants/SysCommon.java b/src/main/java/com/mgm/mcrm/apigateway/common/constants/SysCommon.java index 1e06bbe..7e48d0a 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/common/constants/SysCommon.java +++ b/src/main/java/com/mgm/mcrm/apigateway/common/constants/SysCommon.java @@ -6,6 +6,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.security.KeyStore; import java.util.*; @@ -19,6 +20,10 @@ import org.springframework.boot.system.ApplicationHome; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; + public class SysCommon { @@ -56,7 +61,7 @@ public class SysCommon { bufferedReader = new BufferedReader(fileReader); String lineData = null; int lineCnt =0; - StringBuffer contentBuffer = new StringBuffer(); + StringBuilder contentBuffer = new StringBuilder(); while ((lineData = bufferedReader.readLine()) != null) { if(lineCnt>0) { contentBuffer.append("\n"); @@ -126,4 +131,20 @@ public class SysCommon { } return loginUser; } + + public static X509TrustManager getDefaultTrustManager() { + try { + // 获取 JVM 默认的 TrustManager 作为核心校验逻辑 + TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init((KeyStore) null); // 使用系统的默认信任库 + for (TrustManager tm : tmf.getTrustManagers()) { + if (tm instanceof X509TrustManager) { + return (X509TrustManager) tm; + } + } + }catch(Exception err){ + LogUtil.error(SysCommon.class,"Default X509TrustManager not found["+err.getMessage()+"]"); + } + return null; + } } diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/dtos/config/DaylightApiSetting.java b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/config/DaylightApiSetting.java index 1b0fa5f..632c5a9 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/common/dtos/config/DaylightApiSetting.java +++ b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/config/DaylightApiSetting.java @@ -28,6 +28,7 @@ public class DaylightApiSetting { private String proxyPassword; //--SSL config private String sslBundleName; + private boolean skipTrustValidation=true; private int tokenRequestTimeout; private long tokenExpireSecond; diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/dtos/config/EsbApiSetting.java b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/config/EsbApiSetting.java index dc89ed0..e8d430c 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/common/dtos/config/EsbApiSetting.java +++ b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/config/EsbApiSetting.java @@ -14,8 +14,10 @@ public class EsbApiSetting { private String serverUrl=""; private int apiRequestTimeout; private String tokenUrl=""; - private String appId=""; - private String appSecret=""; + private String appIdMcrm=""; + private String appSecretMcrm=""; + private String appIdLimo=""; + private String appSecretLimo=""; //--Proxy config private String proxyHost; private int proxyPort; @@ -23,6 +25,7 @@ public class EsbApiSetting { private String proxyPassword; //--SSL config private String sslBundleName; + private boolean skipTrustValidation=true; private int tokenRequestTimeout; private long tokenExpireSecond; diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/dtos/config/LimoApiSetting.java b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/config/LimoApiSetting.java new file mode 100644 index 0000000..f8e8214 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/config/LimoApiSetting.java @@ -0,0 +1,38 @@ +package com.mgm.mcrm.apigateway.common.dtos.config; + +import lombok.AccessLevel; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +import java.util.HashMap; +import java.util.Map; + +@Data +public class LimoApiSetting { + //-- Limo API Server + private String serverUrl; + private int apiRequestTimeout; + //--Proxy config + private String proxyHost; + private int proxyPort; + private String proxyUserName; + private String proxyPassword; + //--SSL config + private String sslBundleName; + private boolean skipTrustValidation=true; + + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) + private final Map authorizationConfig = new HashMap<>(); + + public void registerAuthorizationList(Map authItems){ + if(authItems!=null){ + authorizationConfig.putAll(authItems); + } + } + + public String getAppAuthorizatinKey(String appId){ + return authorizationConfig.getOrDefault(appId, null); + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/dtos/config/OperaApiSetting.java b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/config/OperaApiSetting.java index 1573da9..41e398c 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/common/dtos/config/OperaApiSetting.java +++ b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/config/OperaApiSetting.java @@ -21,6 +21,7 @@ public class OperaApiSetting { private String proxyPassword; //--SSL config private String sslBundleName; + private boolean skipTrustValidation=true; @Getter(AccessLevel.NONE) @Setter(AccessLevel.NONE) diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpCookieContext.java b/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpCookieContext.java deleted file mode 100644 index fecc325..0000000 --- a/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpCookieContext.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.mgm.mcrm.apigateway.common.httpclient; - -import org.apache.hc.client5.http.cookie.BasicCookieStore; -import org.apache.hc.client5.http.cookie.CookieStore; -import org.apache.hc.client5.http.protocol.HttpClientContext; - -public class HttpCookieContext { - /** - * 使用httpcontext,用于设置和携带Cookie - */ - private HttpClientContext context ; - - /** - * 储存Cookie - */ - private CookieStore cookieStore; - - public static HttpCookieContext custom(){ - return new HttpCookieContext(); - } - - private HttpCookieContext(){ - this.context = new HttpClientContext(); - this.cookieStore = new BasicCookieStore(); - this.context.setCookieStore(cookieStore); - } - - public HttpClientContext getContext() { - return context; - } - - public HttpCookieContext setContext(HttpClientContext context) { - this.context = context; - return this; - } - - public CookieStore getCookieStore() { - return cookieStore; - } - - public HttpCookieContext setCookieStore(CookieStore cookieStore) { - this.cookieStore = cookieStore; - return this; - } - -} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpEntityBuilder.java b/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpEntityBuilder.java deleted file mode 100644 index 7d77162..0000000 --- a/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpEntityBuilder.java +++ /dev/null @@ -1,310 +0,0 @@ -package com.mgm.mcrm.apigateway.common.httpclient; - -import org.apache.hc.client5.http.entity.UrlEncodedFormEntity; -import org.apache.hc.client5.http.entity.mime.HttpMultipartMode; -import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder; -import org.apache.hc.core5.http.ContentType; -import org.apache.hc.core5.http.HttpEntity; -import org.apache.hc.core5.http.HttpHeaders; -import org.apache.hc.core5.http.NameValuePair; -import org.apache.hc.core5.http.io.entity.ByteArrayEntity; -import org.apache.hc.core5.http.io.entity.FileEntity; -import org.apache.hc.core5.http.io.entity.StringEntity; -import org.apache.hc.core5.http.message.BasicHeader; -import org.apache.hc.core5.http.message.BasicNameValuePair; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.io.UnsupportedEncodingException; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.nio.charset.Charset; -import java.util.*; -import java.util.Map.Entry; - - -public class HttpEntityBuilder { - - //传入参数特定类型 - public static final String ENTITY_STRING="$ENTITY_STRING$"; - public static final String ENTITY_JSON="$ENTITY_JSON$"; - public static final String ENTITY_FILE="$ENTITY_FILEE$"; - public static final String ENTITY_BYTES="$ENTITY_BYTES$"; - public static final String ENTITY_INPUTSTREAM="$ENTITY_INPUTSTREAM$"; - public static final String ENTITY_SERIALIZABLE="$ENTITY_SERIALIZABLE$"; - public static final String ENTITY_MULTIPART="$ENTITY_MULTIPART$"; - private static final List SPECIAL_ENTITIY = Arrays.asList(ENTITY_STRING, ENTITY_JSON, ENTITY_BYTES, ENTITY_FILE, ENTITY_INPUTSTREAM, ENTITY_SERIALIZABLE, ENTITY_MULTIPART); - - /** - * 是否开启debug, - */ - private static boolean debug = false; - - private static final Logger log = LoggerFactory.getLogger(HttpEntityBuilder.class); - - - /** - * 检测url是否含有参数,如果有,则把参数加到参数列表中 - * - * @param url 资源地址 - * @param nvps 参数列表 - * @param encoding 编码 - * @return 返回去掉参数的url - * @throws UnsupportedEncodingException 不支持的编码异常 - */ - public static String checkHasParas(String url, List nvps, String encoding) throws UnsupportedEncodingException { - // 检测url中是否存在参数 - if (url.contains("?") && url.indexOf("?") < url.indexOf("=")) { - String[] paras = url.substring(url.indexOf("?") + 1).split("&"); - for (String para : paras) { - int pos = para.indexOf("="); - if (pos >= 0) { - nvps.add(new BasicNameValuePair(para.substring(0, pos), para.substring(pos + 1))); - } else { - nvps.add(new BasicNameValuePair(para, "")); - } - } - url = url.substring(0, url.indexOf("?")); - } - return url; - } - - /** - * - * 参数转换,将map中的参数,转到参数列表中 - * - * @param nvps 参数列表 - * @param map 参数列表(map) - * @param encoding 编码 - * @return 返回HttpEntity - * @throws UnsupportedEncodingException 不支持的编码异常 - */ - public static HttpEntity map2HttpEntity(List nvps, Map map, String encoding) throws UnsupportedEncodingException { - HttpEntity entity = null; - if(map!=null && map.size()>0){ - boolean isSpecial = false; - // 拼接参数 - for (Entry entry : map.entrySet()) { - if(SPECIAL_ENTITIY.contains(entry.getKey())){//判断是否在之中 - isSpecial = true; - if(ENTITY_STRING.equals(entry.getKey())){//string - entity = new StringEntity(String.valueOf(entry.getValue()), Charset.forName(encoding)); - break; - }else if(ENTITY_JSON.equals(entry.getKey())){//json - ContentType jsonContentType; - - entity = new StringEntity(String.valueOf(entry.getValue()), Charset.forName(encoding)); - String contentType = "application/json"; - if (encoding != null && !encoding.trim().isEmpty()) { - // 创建带有编码的JSON ContentType - jsonContentType = ContentType.APPLICATION_JSON.withCharset(Charset.forName(encoding)); - }else { - // 使用默认的JSON ContentType(通常UTF-8) - jsonContentType = ContentType.APPLICATION_JSON; - } - // 创建实体时直接传入ContentType - entity = new StringEntity(String.valueOf(entry.getValue()), jsonContentType); - break; - }else if(ENTITY_BYTES.equals(entry.getKey())){//file - byte[] data = (byte[]) entry.getValue(); - entity = new ByteArrayEntity(data, ContentType.APPLICATION_OCTET_STREAM); - break; - }else if(ENTITY_FILE.equals(entry.getKey())){//file - if(File.class.isAssignableFrom(entry.getValue().getClass())){ - entity = new FileEntity((File)entry.getValue(), ContentType.APPLICATION_OCTET_STREAM); - }else if(entry.getValue().getClass()==String.class){ - entity = new FileEntity(new File((String) entry.getValue()), ContentType.create("text/plain", "UTF-8")); - } - break; - }else if(ENTITY_INPUTSTREAM.equals(entry.getKey())){//inputstream -// entity = new InputStreamEntity(); - break; - }else if(ENTITY_SERIALIZABLE.equals(entry.getKey())){//serializeable -// entity = new SerializableEntity() - break; - }else if(ENTITY_MULTIPART.equals(entry.getKey())){//MultipartEntityBuilder - File[] files = null; - if(File.class.isAssignableFrom(entry.getValue().getClass().getComponentType())){ - files=(File[])entry.getValue(); - }else if(entry.getValue().getClass().getComponentType()==String.class){ - String[] names = (String[]) entry.getValue(); - files = new File[names.length]; - for (int i = 0; i < names.length; i++) { - files[i] = new File(names[i]); - } - } - MultipartEntityBuilder builder = MultipartEntityBuilder.create(); - builder.setCharset(Charset.forName(encoding));// 设置请求的编码格式 - builder.setMode(HttpMultipartMode.LEGACY);// 设置浏览器兼容模式 - int count = 0; - for (File file : files) { -// //把文件转换成流对象FileBody -// FileBody fileBody = new FileBody(file); -// builder.addPart(String.valueOf(map.get(ENTITY_MULTIPART+".name")) + count++, fileBody); - builder.addBinaryBody(String.valueOf(map.get(ENTITY_MULTIPART+".name")) + count++,file); - } - boolean forceRemoveContentTypeCharset = (boolean)map.get(ENTITY_MULTIPART+".rmCharset"); - Map m = new HashMap(); - m.putAll(map); - m.remove(ENTITY_MULTIPART); - m.remove(ENTITY_MULTIPART+".name"); - m.remove(ENTITY_MULTIPART+".rmCharset"); - Iterator> iterator = m.entrySet().iterator(); - // 发送的数据 - while (iterator.hasNext()) { - Entry e = iterator.next(); - builder.addTextBody(e.getKey(), String.valueOf(e.getValue()), ContentType.create("text/plain", encoding)); - } - entity = builder.build();// 生成 HTTP POST 实体 - - //强制去除contentType中的编码设置,否则,在某些情况下会导致上传失败 - if(forceRemoveContentTypeCharset){ - removeContentTypeCharset(encoding, entity); - } - break; - }else { - nvps.add(new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue()))); - } - }else{ - nvps.add(new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue()))); - } - } - if(!isSpecial) { - entity = new UrlEncodedFormEntity(nvps, Charset.forName(encoding)); - } - } - return entity; - } - - /** - * 移除content-type中的charset - * - * @param encoding 编码 - * @param entity 请求参数及数据信息 - */ - private static void removeContentTypeCharset(String encoding, HttpEntity entity) { - try { - Class clazz = entity.getClass(); - Field field = clazz.getDeclaredField("contentType"); - field.setAccessible(true); //将字段的访问权限设为true:即去除private修饰符的影响 - if(Modifier.isFinal(field.getModifiers())){ - Field modifiersField = Field.class.getDeclaredField("modifiers"); //去除final修饰符的影响,将字段设为可修改的 - modifiersField.setAccessible(true); - modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); - } - BasicHeader o = (BasicHeader) field.get(entity); - field.set(entity, new BasicHeader(HttpHeaders.CONTENT_TYPE, o.getValue().replace("; charset="+encoding,""))); - } catch (NoSuchFieldException e) { - HttpEntityBuilder.exception(e); - } catch (SecurityException e) { - HttpEntityBuilder.exception(e); - } catch (IllegalArgumentException e) { - HttpEntityBuilder.exception(e); - } catch (IllegalAccessException e) { - HttpEntityBuilder.exception(e); - } - } - - - /** - * 生成参数 - * 参数格式:k1=v1&k2=v2 - * - * @param paras 参数列表 - * @return 返回参数列表(map) - */ - public static Map buildParas(String paras){ - String[] p = paras.split("&"); - String[][] ps = new String[p.length][2]; - int pos = 0; - for (int i = 0; i < p.length; i++) { - pos = p[i].indexOf("="); - ps[i][0]=p[i].substring(0,pos); - ps[i][1]=p[i].substring(pos+1); - pos = 0; - } - return buildParas(ps); - } - - /** - * 生成参数 - * 参数类型:{{"k1","v1"},{"k2","v2"}} - * - * @param paras 参数列表 - * @return 返回参数列表(map) - */ - public static Map buildParas(String[][] paras){ - // 创建参数队列 - Map map = new HashMap(); - for (String[] para: paras) { - map.put(para[0], para[1]); - } - return map; - } - - /** - * 打印消息 - * - * @param msg 消息 - */ - public static void info(String msg){ - if(debug){ - log.info(msg); - } - } - - /** - * 打印消息和异常堆栈 - * - * @param msg 异常消息 - * @param t 异常 - */ - public static void infoException(String msg, Throwable t){ - if(debug){ - log.info(msg,t); - } - } - - /** - * 打印错误消息 - * - * @param msg 异常消息 - */ - public static void error(String msg){ - log.error(msg); - } - - /** - * 打印错误消息和异常堆栈 - * - * @param msg 异常消息 - * @param t 异常 - */ - public static void errorException(String msg, Throwable t){ - log.error(msg, t); - } - - /** - * 打印异常堆栈 - * - * @param t 异常 - */ - public static void exception(Throwable t){ - log.error(String.valueOf(t)); - } - - /** - * 开启打印日志 - */ - public static void debug() { - debug(true); - } - /** - * 开启或关闭打印日志 - * @param debug 是否开启debug - */ - public static void debug(boolean debug) { - HttpEntityBuilder.debug = debug; - } -} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpParameterBuilder.java b/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpParameterBuilder.java deleted file mode 100644 index 1e33d0f..0000000 --- a/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpParameterBuilder.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.mgm.mcrm.apigateway.common.httpclient; - - -import com.alibaba.fastjson2.JSON; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * HTTP 参数构建器 - * - * 用于构建 HTTP 请求参数(查询字符串、请求体等)。 - * - * @author Aaka - */ -public class HttpParameterBuilder { - private static final Logger log = LoggerFactory.getLogger(HttpParameterBuilder.class); - - /** - * 將參數 Map 轉換為查詢字符串(用於 GET 請求) - * - * @param params 參數 Map - * @return 查詢字符串,格式為 name1=value1&name2=value2 - */ - public static String buildQueryString(Map params) { - if (params == null || params.isEmpty()) { - return ""; - } - return params.entrySet().stream() - .filter(entry -> entry.getKey() != null && entry.getValue() != null) - .map(entry -> { - try { - String key = URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8); - String value = URLEncoder.encode(String.valueOf(entry.getValue()), StandardCharsets.UTF_8); - return key + "=" + value; - } catch (Exception e) { - log.warn("Failed to encode parameter: {}={}", entry.getKey(), entry.getValue(), e); - return null; - } - }) - .filter(java.util.Objects::nonNull) - .collect(Collectors.joining("&")); - } - - - /** - * 將參數 Map 轉換為請求體字符串 - * - * @param params 參數 Map - * @param contentType 內容類型 - * @return 請求體字符串 - */ - public static String buildRequestBody(Map params, String contentType) { - if (params == null || params.isEmpty()) { - return ""; - } - // 如果是 JSON 格式 - if (contentType != null && contentType.contains("application/json")) { - // 使用 FastJSON 將 Map 轉換為 JSON 字符串 - return JSON.toJSONString(params); - } else { - // 默認使用 form-urlencoded 格式 - return buildQueryString(params); - } - } - -} - diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpRequestOptions.java b/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpRequestOptions.java deleted file mode 100644 index 016d23e..0000000 --- a/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpRequestOptions.java +++ /dev/null @@ -1,375 +0,0 @@ -package com.mgm.mcrm.apigateway.common.httpclient; - -import com.mgm.mcrm.apigateway.common.enums.HttpMethods; -import org.apache.hc.client5.http.classic.HttpClient; -import org.apache.hc.client5.http.config.RequestConfig; -import org.apache.hc.client5.http.entity.mime.Header; -import org.apache.hc.core5.http.protocol.HttpContext; -import org.apache.hc.core5.util.Timeout; - -import java.io.OutputStream; -import java.nio.charset.Charset; -import java.util.HashMap; -import java.util.Map; - -public class HttpRequestOptions { - - private HttpRequestOptions(){}; - - /** - * 获取实例 - * @return 返回当前对象 - */ - public static HttpRequestOptions create(){ - return new HttpRequestOptions(); - } - - - /** - * HttpClient对象 - */ - private HttpClient client; - - /** - * Header头信息 - */ - private Header[] headers; - - /** - * 是否返回response的headers - */ - private boolean isReturnRespHeaders; - - /** - * 请求方法 - */ - private HttpMethods method=HttpMethods.POST; - - /** - * 请求方法名称 - */ - private String methodName; - - /** - * 用于cookie操作 - */ - private HttpContext context; - - // private Map map; - - /** - * 以json格式作为输入参数 - */ - private String json; - - /** - * 输入输出编码 - */ - private String encoding= Charset.defaultCharset().displayName(); - - /** - * 输入编码 - */ - private String inenc; - - /** - * 输出编码 - */ - private String outenc; - - /** - * 设置RequestConfig - */ - private RequestConfig requestConfig; - - /** - * 解决多线程下载时,strean被close的问题 - */ - private static final ThreadLocal outs = new ThreadLocal(); - - /** - * 解决多线程处理时,url被覆盖问题 - */ - private static final ThreadLocal urls = new ThreadLocal(); - - /** - * 解决多线程处理时,url被覆盖问题 - */ - private static final ThreadLocal> maps = new ThreadLocal>(); - - /** - * @param client HttpClient对象 - * @return 返回当前对象 - */ - public HttpRequestOptions client(HttpClient client) { - this.client = client; - return this; - } - - /** - * @param url 资源url - * @return 返回当前对象 - */ - public HttpRequestOptions url(String url) { - urls.set(url); - return this; - } - - /** - * @param headers Header头信息 - * @return 返回当前对象 - */ - public HttpRequestOptions headers(Header[] headers) { - this.headers = headers; - return this; - } - - /** - * Header头信息(是否返回response中的headers) - * - * @param headers Header头信息 - * @param isReturnRespHeaders 是否返回response中的headers - * @return 返回当前对象 - */ - public HttpRequestOptions headers(Header[] headers, boolean isReturnRespHeaders) { - this.headers = headers; - this.isReturnRespHeaders=isReturnRespHeaders; - return this; - } - - /** - * @param method 请求方法 - * @return 返回当前对象 - */ - public HttpRequestOptions method(HttpMethods method) { - this.method = method; - return this; - } - - /** - * @param methodName 请求方法 - * @return 返回当前对象 - */ - public HttpRequestOptions methodName(String methodName) { - this.methodName = methodName; - return this; - } - - /** - * @param context cookie操作相关 - * @return 返回当前对象 - */ - public HttpRequestOptions context(HttpContext context) { - this.context = context; - return this; - } - - /** - * @param map 传递参数 - * @return 返回当前对象 - */ - public HttpRequestOptions map(Map map) { -// synchronized (getClass()) { -// if(this.map==null || map==null){ -// this.map = map; -// }else { -// this.map.putAll(map);; -// } -// } - Map m = maps.get(); - if(m == null || map.isEmpty()){ - m = map; - }else { - m.putAll(map); - } - maps.set(m); - return this; - } - - /** - * @param json 以json格式字符串作为参数 - * @return 返回当前对象 - */ - public HttpRequestOptions json(String json) { - this.json = json; - Map map = new HashMap(); - map.put(HttpEntityBuilder.ENTITY_JSON, json); - maps.set(map); - return this; - } - - /** - * @param filePaths 待上传文件所在路径 - * @return 返回当前对象 - */ - public HttpRequestOptions files(String[] filePaths) { - return files(filePaths, "file"); - } - /** - * 上传文件时用到 - * @param filePaths 待上传文件所在路径 - * @param inputName 即file input 标签的name值,默认为file - * @return 返回当前对象 - */ - public HttpRequestOptions files(String[] filePaths, String inputName) { - return files(filePaths, inputName, false); - } - /** - * 上传文件时用到 - * @param filePaths 待上传文件所在路径 - * @param inputName 即file input 标签的name值,默认为file - * @param forceRemoveContentTypeChraset 是否强制一处content-type中设置的编码类型 - * @return 返回当前对象 - */ - public HttpRequestOptions files(String[] filePaths, String inputName, boolean forceRemoveContentTypeChraset) { -// synchronized (getClass()) { -// if(this.map==null){ -// this.map= new HashMap(); -// } -// } -// map.put(Utils.ENTITY_MULTIPART, filePaths); -// map.put(Utils.ENTITY_MULTIPART+".name", inputName); -// map.put(Utils.ENTITY_MULTIPART+".rmCharset", forceRemoveContentTypeChraset); - - Map m = maps.get(); - if(m.isEmpty()){ - m = new HashMap(); - } - m.put(HttpEntityBuilder.ENTITY_MULTIPART, filePaths); - m.put(HttpEntityBuilder.ENTITY_MULTIPART+".name", inputName); - m.put(HttpEntityBuilder.ENTITY_MULTIPART+".rmCharset", forceRemoveContentTypeChraset); - maps.set(m); - return this; - } - - /** - * @param encoding 输入输出编码 - * @return 返回当前对象 - */ - public HttpRequestOptions encoding(String encoding) { - //设置输入输出 - inenc(encoding); - outenc(encoding); - this.encoding = encoding; - return this; - } - - /** - * @param inenc 输入编码 - */ - public void inenc(String inenc) { - this.inenc = inenc; - } - - /** - * @param outenc 输出编码 - */ - public void outenc(String outenc) { - this.outenc = outenc; - } - - /** - * @param out 输出流对象 - * @return 返回当前对象 - */ - public HttpRequestOptions out(OutputStream out) { - outs.set(out); - return this; - } - - /** - * 设置超时时间 - * - * @param timeout 超市时间,单位-毫秒 - * @return 返回当前对象 - */ - public HttpRequestOptions timeout(int timeout){ - return timeout(timeout, true); - } - - /** - * 设置超时时间以及是否允许网页重定向(自动跳转 302) - * - * @param timeout 超时时间,单位-毫秒 - * @param redirectEnable 自动跳转 - * @return 返回当前对象 - */ - public HttpRequestOptions timeout(int timeout, boolean redirectEnable){ - // 配置请求的超时设置 - // 注意:HttpClient 5 中需要使用 Timeout 对象,而不是直接的 int 值 - Timeout timeoutValue = Timeout.ofMilliseconds(timeout); - RequestConfig config = RequestConfig.custom() - .setConnectionRequestTimeout(timeoutValue) - .setResponseTimeout(timeoutValue) - .setRedirectsEnabled(redirectEnable) - .build(); - return timeout(config); - } - - /** - * 设置代理、超时时间、允许网页重定向等 - * - * @param requestConfig 超时时间,单位-毫秒 - * @return 返回当前对象 - */ - public HttpRequestOptions timeout(RequestConfig requestConfig){ - this.requestConfig = requestConfig; - return this; - } - - public HttpClient client() { - return client; - } - - public Header[] headers() { - return headers; - } - public boolean isReturnRespHeaders() { - return isReturnRespHeaders; - } - - public String url() { - return urls.get(); - } - - public HttpMethods method() { - return method; - } - - public String methodName() { - return methodName; - } - - public HttpContext context() { - return context; - } - - public Map map() { -// return map; - return maps.get(); - } - - public String json() { - return json; - } - - public String encoding() { - return encoding; - } - - public String inenc() { - return inenc == null ? encoding : inenc; - } - - public String outenc() { - return outenc == null ? encoding : outenc; - } - - public OutputStream out() { - return outs.get(); - } - - public RequestConfig requestConfig() { - return requestConfig; - } -} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpSslSupport.java b/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpSslSupport.java deleted file mode 100644 index b6e003d..0000000 --- a/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpSslSupport.java +++ /dev/null @@ -1,197 +0,0 @@ -package com.mgm.mcrm.apigateway.common.httpclient; - -import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory; -import org.apache.hc.core5.ssl.SSLContexts; - -import javax.net.ssl.*; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.security.KeyManagementException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; - -/** - * SSL 工具类(基于 HttpClient 5) - * - * 提供: - * - 信任所有证书的 SSLSocketFactory - * - 信任所有证书的 SSLConnectionSocketFactory - * - * 注意:默认实现信任所有证书,仅适用于内部系统或测试环境。 - * - * @author Anka - * @version 2.0 (client5) - */ -public class HttpSslSupport { - private static final SSLHandler SIMPLE_VERIFIER = new SSLHandler(); - - private static SSLSocketFactory sslFactory; - // 注意:SSLConnectionSocketFactory 在 HttpClient 5 中被标记为 deprecated, - // 但这是目前唯一可用的方式,新的 API 尚未稳定 - @SuppressWarnings("deprecation") - private static SSLConnectionSocketFactory sslConnFactory; - - private static final HttpSslSupport INSTANCE = new HttpSslSupport(); - - private SSLContext sc; - - public static HttpSslSupport getInstance() { - return INSTANCE; - } - - public static HttpSslSupport custom() { - return new HttpSslSupport(); - } - - /** - * 重写 X509TrustManager 和 HostnameVerifier,信任所有证书与主机名。 - * 仅推荐在内部环境或调试场景使用。 - */ - private static class SSLHandler implements X509TrustManager, HostnameVerifier { - - @Override - public java.security.cert.X509Certificate[] getAcceptedIssuers() { - return new java.security.cert.X509Certificate[]{}; - } - - @Override - public void checkServerTrusted(java.security.cert.X509Certificate[] chain, - String authType) throws CertificateException { - // 信任所有服务器证书 - } - - @Override - public void checkClientTrusted(java.security.cert.X509Certificate[] chain, - String authType) throws CertificateException { - // 信任所有客户端证书 - } - - @Override - public boolean verify(String paramString, SSLSession paramSSLSession) { - // 信任所有主机名 - return true; - } - } - - /** - * 获取默认 HostnameVerifier(信任所有主机名) - */ - public static HostnameVerifier getVerifier() { - return SIMPLE_VERIFIER; - } - - /** - * 获取信任所有证书的 {@link SSLSocketFactory} - */ - public synchronized SSLSocketFactory getSSLSF(SSLProtocolVersion sslpv) { - if (sslFactory != null) { - return sslFactory; - } - try { - SSLContext context = getSSLContext(sslpv); - context.init(null, new TrustManager[]{SIMPLE_VERIFIER}, null); - sslFactory = context.getSocketFactory(); - return sslFactory; - } catch (KeyManagementException e) { - throw new RuntimeException("初始化 SSLSocketFactory 失败", e); - } - } - - /** - * 获取信任所有证书的 {@link SSLConnectionSocketFactory}(HttpClient5) - * - * 注意:SSLConnectionSocketFactory 在 HttpClient 5 中被标记为 deprecated, - * 但这是目前唯一可用的方式,新的 API 尚未稳定 - */ - @SuppressWarnings("deprecation") - public synchronized SSLConnectionSocketFactory getSSLCONNSF(SSLProtocolVersion sslpv) { - if (sslConnFactory != null) { - return sslConnFactory; - } - try { - SSLContext context = getSSLContext(sslpv); - context.init(null, new TrustManager[]{SIMPLE_VERIFIER}, new java.security.SecureRandom()); - sslConnFactory = new SSLConnectionSocketFactory(context, SIMPLE_VERIFIER); - return sslConnFactory; - } catch (KeyManagementException e) { - throw new RuntimeException("初始化 SSLConnectionSocketFactory 失败", e); - } - } - - /** - * 使用自定义证书(trustStore)初始化 SSLContext - */ - public HttpSslSupport customSSL(String keyStorePath, String keyStorepass) { - FileInputStream instream = null; - try { - KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); - instream = new FileInputStream(new File(keyStorePath)); - trustStore.load(instream, keyStorepass.toCharArray()); - // 相信自己的 CA 和所有自签名证书 - this.sc = SSLContexts.custom() - .loadTrustMaterial(trustStore, (chain, authType) -> true) - .build(); - return this; - } catch (KeyManagementException | KeyStoreException | - NoSuchAlgorithmException | CertificateException | IOException e) { - throw new RuntimeException("初始化自定义 SSLContext 失败", e); - } finally { - if (instream != null) { - try { - instream.close(); - } catch (IOException ignore) { - // ignore - } - } - } - } - - /** - * 获取(或创建)指定协议版本的 {@link SSLContext} - */ - public SSLContext getSSLContext(SSLProtocolVersion sslpv) { - try { - if (this.sc == null) { - this.sc = SSLContext.getInstance(sslpv.getName()); - } - return this.sc; - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException("不支持的 SSL 协议版本: " + sslpv.getName(), e); - } - } - - /** - * 支持的 SSL/TLS 协议版本枚举 - */ - public enum SSLProtocolVersion { - SSL("SSL"), - SSLv3("SSLv3"), - TLSv1("TLSv1"), - TLSv1_1("TLSv1.1"), - TLSv1_2("TLSv1.2"), - TLSv1_3("TLSv1.3"); - - private final String name; - - SSLProtocolVersion(String name) { - this.name = name; - } - - public String getName() { - return this.name; - } - - public static SSLProtocolVersion find(String name) { - for (SSLProtocolVersion pv : SSLProtocolVersion.values()) { - if (pv.getName().equalsIgnoreCase(name)) { - return pv; - } - } - throw new IllegalArgumentException("未支持当前 SSL 版本号:" + name); - } - - } -} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/listener/ConfigListener.java b/src/main/java/com/mgm/mcrm/apigateway/common/listener/ConfigListener.java index 1f038a0..1a9a66c 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/common/listener/ConfigListener.java +++ b/src/main/java/com/mgm/mcrm/apigateway/common/listener/ConfigListener.java @@ -2,7 +2,9 @@ package com.mgm.mcrm.apigateway.common.listener; import com.mgm.mcrm.apigateway.allotment.services.RoomCriteriaDaylightService; import com.mgm.mcrm.apigateway.common.constants.SysCommon; +import com.mgm.mcrm.apigateway.common.dtos.config.DaylightApiSetting; import com.mgm.mcrm.apigateway.common.utils.NotificationUtil; +import com.mgm.mcrm.apigateway.daylight.common.httpclient.HttpSslSupport; import com.mgm.mcrm.apigateway.daylight.repositories.CfgCompRuleDao; import com.mgm.mcrm.apigateway.daylight.services.DaylightApiService; import com.mgm.mcrm.apigateway.daylight.webhook.eventhandlers.impl.AvailableRoomCountUpdatedHandler; @@ -15,7 +17,10 @@ import com.mgm.mcrm.apigateway.notification.services.ApnNotificationService; import com.mgm.mcrm.apigateway.reservation.repositories.BookingDao; import com.mgm.mcrm.apigateway.reservation.services.BookingService; import jakarta.annotation.Resource; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.event.ApplicationStartedEvent; +import org.springframework.boot.ssl.SslBundles; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; @@ -28,16 +33,12 @@ public class ConfigListener implements ApplicationListener interalIpPatterns = new ArrayList(); + private static final List interalIpPatterns = new ArrayList<>(); private static final Pattern IPV4_PATTERN = Pattern.compile("^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"); private static final List INTERNAL_IP_PREFIXES = Arrays.asList( @@ -24,6 +21,13 @@ public class NetworkUtil { "172.20.", "172.21.", "172.22.", "172.23.", "172.24.", "172.25.", "172.26.", "172.27.", "172.28.", "172.29.", "172.30.", "172.31." ); + private static final Set ALLOWED_PROTOCOLS = new HashSet<>( + Arrays.asList("http", "https") + ); + // 禁止的端口 + private static final Set BLOCKED_PORTS = new HashSet<>( + Arrays.asList(22, 23, 25, 53, 110, 143, 3306, 5432, 6379, 27017) + ); public static void setInternalIpPattern(String patternConfig) { interalIpPatterns.clear(); @@ -196,22 +200,22 @@ public class NetworkUtil { // 外网IP String netip = null; try { - Enumeration netInterfaces = NetworkInterface.getNetworkInterfaces(); + Enumeration netInterfaces = NetworkInterface.getNetworkInterfaces(); InetAddress ip = null; // 是否找到外网IP boolean finded = false; while (netInterfaces.hasMoreElements() && !finded) { NetworkInterface ni = (NetworkInterface) netInterfaces.nextElement(); - Enumeration address = ni.getInetAddresses(); + Enumeration address = ni.getInetAddresses(); while (address.hasMoreElements()) { ip = (InetAddress) address.nextElement(); - if (!ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && ip.getHostAddress().indexOf(":") == -1) { + if (!ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && !ip.getHostAddress().contains(":")) { // 外网IP netip = ip.getHostAddress(); finded = true; break; } else if (ip.isSiteLocalAddress() && !ip.isLoopbackAddress() - && ip.getHostAddress().indexOf(":") == -1) { + && !ip.getHostAddress().contains(":")) { // 内网IP localip = ip.getHostAddress(); } @@ -316,40 +320,106 @@ public class NetworkUtil { return false; } - public static boolean isValidURL(String requestUrl){ + public static String sanitizeCRLF(String input){ + if(input==null) + return null; + return input.replaceAll("[\r\n]+", ""); + } + + public static String validateAndSanitizeUrl(String userInputUrl) throws SecurityException { + if (userInputUrl == null || userInputUrl.trim().isEmpty()) { + throw new SecurityException("URL should not be blank"); + } + try { - URI uri = new URI(requestUrl); - String scheme = uri.getScheme(); - String host = uri.getHost(); - if(!"https".equals(scheme) && !"http".equals(scheme)) { - return false; + // 1. 解析URL + URL url = new URI(userInputUrl).toURL(); + + // 2. 验证协议 + validateProtocol(url.getProtocol()); + + // 3. 验证主机名/IP + String host = url.getHost(); + validateHost(host); + + // 4. 验证端口 + int port = url.getPort(); + if (port == -1) { + port = url.getDefaultPort(); } - if(host.matches("\\d+\\.\\d+\\.\\d+\\.\\d+")) { - return false; - } - return true; + validatePort(port); + + // 5. 验证路径(防止路径遍历) + validatePath(url.getPath()); + + // 6. 返回净化后的URL(标准化) + return sanitizeUrl(url); + } catch (Exception e) { - return false; + throw new SecurityException("Invalid URL: " + e.getMessage(), e); } } - public static String encodeParaWithSpring(String value) { - if(value==null) - value=""; - value= value.replace("\r", "").replace("\n", ""); - return UriUtils.encode(value, StandardCharsets.UTF_8); + private static void validateProtocol(String protocol) throws SecurityException { + if (!ALLOWED_PROTOCOLS.contains(protocol.toLowerCase())) { + throw new SecurityException("No support protocol: " + protocol + ", only for HTTP/HTTPS"); + } } - public static String sanitizeHeaderValue(String headerValue) { - return headerValue==null? null :(StringEscapeUtils.escapeJava( - headerValue.replace("\r", "").replace("\n", "") - )); + private static void validateHost(String host) throws SecurityException { + if (host == null || host.isEmpty()) { + throw new SecurityException("Host name can not be empty"); + } + + // 解析IP并验证 + try { + InetAddress address = InetAddress.getByName(host); + String ip = address.getHostAddress(); + + if (address.isLoopbackAddress()) { + throw new SecurityException("Forbit loopback address"); + } + + } catch (UnknownHostException e) { + throw new SecurityException("Invalid host: " + host); + } } - public static String encodePathWithSpring(String value) { - if(value==null) - value=""; - value= value.replace("\r", "").replace("\n", ""); - return UriUtils.encodePath(value, StandardCharsets.UTF_8); + private static void validatePort(int port) throws SecurityException { + // 检查是否为禁止端口 + if (BLOCKED_PORTS.contains(port)) { + throw new SecurityException("Invalid Port: " + port); + } + } + + private static void validatePath(String path) throws SecurityException { + if (path == null) return; + + // 防止路径遍历攻击 + if (path.contains("..")) { + throw new SecurityException("Invalid path contains '..'"); + } + if (path.toLowerCase().contains("://")) { + throw new SecurityException("Invalid path prefix"); + } + } + + private static String sanitizeUrl(URL url) { + StringBuilder sb = new StringBuilder(); + sb.append(url.getProtocol()).append("://"); + sb.append(url.getHost()); + + int port = url.getPort(); + if (port != -1 && port != url.getDefaultPort()) { + sb.append(":").append(port); + } + + sb.append(url.getPath()); + + String query = url.getQuery(); + if (query != null && !query.isEmpty()) { + sb.append("?").append(query); + } + return sb.toString(); } } diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/utils/mockup/HttpUtilMockup.java b/src/main/java/com/mgm/mcrm/apigateway/common/utils/mockup/HttpUtilMockup.java index 9fd9f54..900ea80 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/common/utils/mockup/HttpUtilMockup.java +++ b/src/main/java/com/mgm/mcrm/apigateway/common/utils/mockup/HttpUtilMockup.java @@ -97,7 +97,7 @@ public class HttpUtilMockup { private static boolean matchCondiction(JSONObject requestObj, String condiction) { boolean matchResult = false; - List operateList = new ArrayList(); + List operateList = new ArrayList<>(); String[] andList = condiction.split("&&"); for(int i=0;i0) { @@ -126,26 +126,25 @@ public class HttpUtilMockup { } String lastOperate = "AND"; boolean isFirst =true; - for(int i=0;i=")) { - result = (numVal1>=numVal2); - }else if(operate.equals("<")) { - result = (numVal1")) { - result = (numVal1>numVal2); - } + if(isNum) { + result = switch (operate) { + case "==" -> (numVal1 == numVal2); + case "<=" -> (numVal1 <= numVal2); + case ">=" -> (numVal1 >= numVal2); + case "<" -> (numVal1 < numVal2); + case ">" -> (numVal1 > numVal2); + default -> result; + }; }else { String textVal1 = valName1; String textVal2 = valName2; @@ -233,17 +229,14 @@ public class HttpUtilMockup { if(requestObj.containsKey(valName2)) { textVal2 = requestObj.getString(valName2); } - if(operate.equals("==")) { - result = (textVal1.compareTo(textVal2)==0); - }else if(operate.equals("<=")) { - result = (textVal1.compareTo(textVal2)<=0); - }else if(operate.equals(">=")) { - result = (textVal1.compareTo(textVal2)>=0); - }else if(operate.equals("<")) { - result = (textVal1.compareTo(textVal2)<0); - }else if(operate.equals(">")) { - result = (textVal1.compareTo(textVal2)>0); - } + result = switch (operate) { + case "==" -> (textVal1.compareTo(textVal2) == 0); + case "<=" -> (textVal1.compareTo(textVal2) <= 0); + case ">=" -> (textVal1.compareTo(textVal2) >= 0); + case "<" -> (textVal1.compareTo(textVal2) < 0); + case ">" -> (textVal1.compareTo(textVal2) > 0); + default -> result; + }; } } return result; diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/common/httpclient/HttpEntityBuilder.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/httpclient/HttpEntityBuilder.java index b899070..4251118 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/daylight/common/httpclient/HttpEntityBuilder.java +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/httpclient/HttpEntityBuilder.java @@ -85,86 +85,91 @@ public class HttpEntityBuilder { if(map!=null && map.size()>0){ boolean isSpecial = false; // 拼接参数 + label: for (Entry entry : map.entrySet()) { if(SPECIAL_ENTITIY.contains(entry.getKey())){//判断是否在之中 isSpecial = true; - if(ENTITY_STRING.equals(entry.getKey())){//string - entity = new StringEntity(String.valueOf(entry.getValue()), Charset.forName(encoding)); - break; - }else if(ENTITY_JSON.equals(entry.getKey())){//json - ContentType jsonContentType; + switch (entry.getKey()) { + case ENTITY_STRING: //string + entity = new StringEntity(String.valueOf(entry.getValue()), Charset.forName(encoding)); + break label; + case ENTITY_JSON: //json + ContentType jsonContentType; - entity = new StringEntity(String.valueOf(entry.getValue()), Charset.forName(encoding)); - String contentType = "application/json"; - if (encoding != null && !encoding.trim().isEmpty()) { - // 创建带有编码的JSON ContentType - jsonContentType = ContentType.APPLICATION_JSON.withCharset(Charset.forName(encoding)); - }else { - // 使用默认的JSON ContentType(通常UTF-8) - jsonContentType = ContentType.APPLICATION_JSON; - } - // 创建实体时直接传入ContentType - entity = new StringEntity(String.valueOf(entry.getValue()), jsonContentType); - break; - }else if(ENTITY_BYTES.equals(entry.getKey())){//file - byte[] data = (byte[]) entry.getValue(); - entity = new ByteArrayEntity(data, ContentType.APPLICATION_OCTET_STREAM); - break; - }else if(ENTITY_FILE.equals(entry.getKey())){//file - if(File.class.isAssignableFrom(entry.getValue().getClass())){ - entity = new FileEntity((File)entry.getValue(), ContentType.APPLICATION_OCTET_STREAM); - }else if(entry.getValue().getClass()==String.class){ - entity = new FileEntity(new File((String) entry.getValue()), ContentType.create("text/plain", "UTF-8")); - } - break; - }else if(ENTITY_INPUTSTREAM.equals(entry.getKey())){//inputstream -// entity = new InputStreamEntity(); - break; - }else if(ENTITY_SERIALIZABLE.equals(entry.getKey())){//serializeable -// entity = new SerializableEntity() - break; - }else if(ENTITY_MULTIPART.equals(entry.getKey())){//MultipartEntityBuilder - File[] files = null; - if(File.class.isAssignableFrom(entry.getValue().getClass().getComponentType())){ - files=(File[])entry.getValue(); - }else if(entry.getValue().getClass().getComponentType()==String.class){ - String[] names = (String[]) entry.getValue(); - files = new File[names.length]; - for (int i = 0; i < names.length; i++) { - files[i] = new File(names[i]); + entity = new StringEntity(String.valueOf(entry.getValue()), Charset.forName(encoding)); + String contentType = "application/json"; + if (encoding != null && !encoding.trim().isEmpty()) { + // 创建带有编码的JSON ContentType + jsonContentType = ContentType.APPLICATION_JSON.withCharset(Charset.forName(encoding)); + } else { + // 使用默认的JSON ContentType(通常UTF-8) + jsonContentType = ContentType.APPLICATION_JSON; } - } - MultipartEntityBuilder builder = MultipartEntityBuilder.create(); - builder.setCharset(Charset.forName(encoding));// 设置请求的编码格式 - builder.setMode(HttpMultipartMode.LEGACY);// 设置浏览器兼容模式 - int count = 0; - for (File file : files) { + // 创建实体时直接传入ContentType + entity = new StringEntity(String.valueOf(entry.getValue()), jsonContentType); + break label; + case ENTITY_BYTES: //file + byte[] data = (byte[]) entry.getValue(); + entity = new ByteArrayEntity(data, ContentType.APPLICATION_OCTET_STREAM); + break label; + case ENTITY_FILE: //file + if (File.class.isAssignableFrom(entry.getValue().getClass())) { + entity = new FileEntity((File) entry.getValue(), ContentType.APPLICATION_OCTET_STREAM); + } else if (entry.getValue().getClass() == String.class) { + entity = new FileEntity(new File((String) entry.getValue()), ContentType.create("text/plain", "UTF-8")); + } + break label; + case ENTITY_INPUTSTREAM: //input stream +// entity = new InputStreamEntity(); + break label; + case ENTITY_SERIALIZABLE: //serializable +// entity = new SerializableEntity() + break label; + case ENTITY_MULTIPART: //MultipartEntityBuilder + File[] files = null; + if (File.class.isAssignableFrom(entry.getValue().getClass().getComponentType())) { + files = (File[]) entry.getValue(); + } else if (entry.getValue().getClass().getComponentType() == String.class) { + String[] names = (String[]) entry.getValue(); + files = new File[names.length]; + for (int i = 0; i < names.length; i++) { + files[i] = new File(names[i]); + } + } + MultipartEntityBuilder builder = MultipartEntityBuilder.create(); + builder.setCharset(Charset.forName(encoding));// 设置请求的编码格式 + + builder.setMode(HttpMultipartMode.LEGACY);// 设置浏览器兼容模式 + + int count = 0; + for (File file : files) { // //把文件转换成流对象FileBody // FileBody fileBody = new FileBody(file); // builder.addPart(String.valueOf(map.get(ENTITY_MULTIPART+".name")) + count++, fileBody); - builder.addBinaryBody(String.valueOf(map.get(ENTITY_MULTIPART+".name")) + count++,file); - } - boolean forceRemoveContentTypeCharset = (boolean)map.get(ENTITY_MULTIPART+".rmCharset"); - Map m = new HashMap(); - m.putAll(map); - m.remove(ENTITY_MULTIPART); - m.remove(ENTITY_MULTIPART+".name"); - m.remove(ENTITY_MULTIPART+".rmCharset"); - Iterator> iterator = m.entrySet().iterator(); - // 发送的数据 - while (iterator.hasNext()) { - Entry e = iterator.next(); - builder.addTextBody(e.getKey(), String.valueOf(e.getValue()), ContentType.create("text/plain", encoding)); - } - entity = builder.build();// 生成 HTTP POST 实体 + builder.addBinaryBody(String.valueOf(map.get(ENTITY_MULTIPART + ".name")) + count++, file); + } + boolean forceRemoveContentTypeCharset = (boolean) map.get(ENTITY_MULTIPART + ".rmCharset"); + Map m = new HashMap<>(); + m.putAll(map); + m.remove(ENTITY_MULTIPART); + m.remove(ENTITY_MULTIPART + ".name"); + m.remove(ENTITY_MULTIPART + ".rmCharset"); + // 发送的数据 + for (Entry e : m.entrySet()) { + builder.addTextBody(e.getKey(), String.valueOf(e.getValue()), ContentType.create("text/plain", encoding)); + } + entity = builder.build();// 生成 HTTP POST 实体 - //强制去除contentType中的编码设置,否则,在某些情况下会导致上传失败 - if(forceRemoveContentTypeCharset){ - removeContentTypeCharset(encoding, entity); - } - break; - }else { - nvps.add(new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue()))); + + //强制去除contentType中的编码设置,否则,在某些情况下会导致上传失败 + if (forceRemoveContentTypeCharset) { + removeContentTypeCharset(encoding, entity); + } + break label; + case null: + default: + nvps.add(new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue()))); + break; } }else{ nvps.add(new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue()))); @@ -195,13 +200,7 @@ public class HttpEntityBuilder { } BasicHeader o = (BasicHeader) field.get(entity); field.set(entity, new BasicHeader(HttpHeaders.CONTENT_TYPE, o.getValue().replace("; charset="+encoding,""))); - } catch (NoSuchFieldException e) { - HttpEntityBuilder.exception(e); - } catch (SecurityException e) { - HttpEntityBuilder.exception(e); - } catch (IllegalArgumentException e) { - HttpEntityBuilder.exception(e); - } catch (IllegalAccessException e) { + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { HttpEntityBuilder.exception(e); } } @@ -236,7 +235,7 @@ public class HttpEntityBuilder { */ public static Map buildParas(String[][] paras){ // 创建参数队列 - Map map = new HashMap(); + Map map = new HashMap<>(); for (String[] para: paras) { map.put(para[0], para[1]); } diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpHeaderBuilder.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/httpclient/HttpHeaderBuilder.java similarity index 99% rename from src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpHeaderBuilder.java rename to src/main/java/com/mgm/mcrm/apigateway/daylight/common/httpclient/HttpHeaderBuilder.java index d01c747..d0abcca 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpHeaderBuilder.java +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/httpclient/HttpHeaderBuilder.java @@ -1,4 +1,4 @@ -package com.mgm.mcrm.apigateway.common.httpclient; +package com.mgm.mcrm.apigateway.daylight.common.httpclient; import com.mgm.mcrm.apigateway.common.config.HttpHeader; @@ -25,7 +25,7 @@ public class HttpHeaderBuilder { } //记录head头信息 - private Map headerMaps = new HashMap(); + private Map headerMaps = new HashMap<>(); /** * 自定义header头信息 diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/common/httpclient/HttpRequestOptions.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/httpclient/HttpRequestOptions.java index 68dea0b..75b38e7 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/daylight/common/httpclient/HttpRequestOptions.java +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/httpclient/HttpRequestOptions.java @@ -85,17 +85,17 @@ public class HttpRequestOptions { /** * 解决多线程下载时,strean被close的问题 */ - private static final ThreadLocal outs = new ThreadLocal(); + private static final ThreadLocal outs = new ThreadLocal<>(); /** * 解决多线程处理时,url被覆盖问题 */ - private static final ThreadLocal urls = new ThreadLocal(); + private static final ThreadLocal urls = new ThreadLocal<>(); /** * 解决多线程处理时,url被覆盖问题 */ - private static final ThreadLocal> maps = new ThreadLocal>(); + private static final ThreadLocal> maps = new ThreadLocal<>(); /** * @param client HttpClient对象 @@ -169,13 +169,6 @@ public class HttpRequestOptions { * @return 返回当前对象 */ public HttpRequestOptions map(Map map) { -// synchronized (getClass()) { -// if(this.map==null || map==null){ -// this.map = map; -// }else { -// this.map.putAll(map);; -// } -// } Map m = maps.get(); if(m == null || map.isEmpty()){ m = map; @@ -222,18 +215,10 @@ public class HttpRequestOptions { * @return 返回当前对象 */ public HttpRequestOptions files(String[] filePaths, String inputName, boolean forceRemoveContentTypeChraset) { -// synchronized (getClass()) { -// if(this.map==null){ -// this.map= new HashMap(); -// } -// } -// map.put(Utils.ENTITY_MULTIPART, filePaths); -// map.put(Utils.ENTITY_MULTIPART+".name", inputName); -// map.put(Utils.ENTITY_MULTIPART+".rmCharset", forceRemoveContentTypeChraset); Map m = maps.get(); if(m.isEmpty()){ - m = new HashMap(); + m = new HashMap<>(); } m.put(HttpEntityBuilder.ENTITY_MULTIPART, filePaths); m.put(HttpEntityBuilder.ENTITY_MULTIPART+".name", inputName); diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/common/httpclient/HttpSslSupport.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/httpclient/HttpSslSupport.java index 3500bdc..ae4b4f5 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/daylight/common/httpclient/HttpSslSupport.java +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/httpclient/HttpSslSupport.java @@ -1,7 +1,14 @@ package com.mgm.mcrm.apigateway.daylight.common.httpclient; +import com.mgm.mcrm.apigateway.common.constants.SysCommon; +import com.mgm.mcrm.apigateway.common.services.ConfigurableTrustHandler; +import lombok.Getter; +import lombok.Setter; import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory; import org.apache.hc.core5.ssl.SSLContexts; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ssl.SslBundle; +import org.springframework.boot.ssl.SslBundles; import javax.net.ssl.*; import java.io.File; @@ -26,7 +33,16 @@ import java.security.cert.CertificateException; * @version 2.0 (client5) */ public class HttpSslSupport { - private static final SSLHandler SIMPLE_VERIFIER = new SSLHandler(); + + @Getter + @Setter + private static boolean skipValidation=true; + + private static ConfigurableTrustHandler trustHandler=null; + + @Getter + @Setter + private static SslBundle sslBundle=null; private static SSLSocketFactory sslFactory; // 注意:SSLConnectionSocketFactory 在 HttpClient 5 中被标记为 deprecated, @@ -46,41 +62,13 @@ public class HttpSslSupport { return new HttpSslSupport(); } - /** - * 重写 X509TrustManager 和 HostnameVerifier,信任所有证书与主机名。 - * 仅推荐在内部环境或调试场景使用。 - */ - private static class SSLHandler implements X509TrustManager, HostnameVerifier { - @Override - public java.security.cert.X509Certificate[] getAcceptedIssuers() { - return new java.security.cert.X509Certificate[]{}; + + public static ConfigurableTrustHandler getTrustHandler(){ + if(trustHandler==null){ + trustHandler =new ConfigurableTrustHandler(skipValidation, SysCommon.getDefaultTrustManager()); } - - @Override - public void checkServerTrusted(java.security.cert.X509Certificate[] chain, - String authType) throws CertificateException { - // 信任所有服务器证书 - } - - @Override - public void checkClientTrusted(java.security.cert.X509Certificate[] chain, - String authType) throws CertificateException { - // 信任所有客户端证书 - } - - @Override - public boolean verify(String paramString, SSLSession paramSSLSession) { - // 信任所有主机名 - return true; - } - } - - /** - * 获取默认 HostnameVerifier(信任所有主机名) - */ - public static HostnameVerifier getVerifier() { - return SIMPLE_VERIFIER; + return trustHandler; } /** @@ -91,8 +79,15 @@ public class HttpSslSupport { return sslFactory; } try { - SSLContext context = getSSLContext(sslpv); - context.init(null, new TrustManager[]{SIMPLE_VERIFIER}, null); + SSLContext context =null; + if(sslBundle!=null){ + context = sslBundle.createSslContext(); + }else{ + context = getSSLContext(sslpv); + context.init(null, new TrustManager[]{ + getTrustHandler() + }, null); + } sslFactory = context.getSocketFactory(); return sslFactory; } catch (KeyManagementException e) { @@ -112,9 +107,17 @@ public class HttpSslSupport { return sslConnFactory; } try { - SSLContext context = getSSLContext(sslpv); - context.init(null, new TrustManager[]{SIMPLE_VERIFIER}, new java.security.SecureRandom()); - sslConnFactory = new SSLConnectionSocketFactory(context, SIMPLE_VERIFIER); + SSLContext context = null; + if (sslBundle != null) { + context = sslBundle.createSslContext(); + } else { + context = getSSLContext(sslpv); + ConfigurableTrustHandler trustHandler = getTrustHandler(); + context.init(null, new TrustManager[]{ + trustHandler + }, new java.security.SecureRandom()); + } + sslConnFactory = new SSLConnectionSocketFactory(context); return sslConnFactory; } catch (KeyManagementException e) { throw new RuntimeException("初始化 SSLConnectionSocketFactory 失败", e); diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/common/services/HttpService.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/services/HttpService.java index 3023f9e..49d9c07 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/daylight/common/services/HttpService.java +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/services/HttpService.java @@ -166,17 +166,4 @@ public class HttpService { log.info("PATCH request result={}", result); return result; } - -// public HttpResult sslSendNotification(String url, Header[] headers, SendNotificationRequest request) throws Exception { -// log.info("POST method, send notification, url={}, headers={}, map={}", notificationProxyUrl + url, JSON.toJSONString(headers), JSON.toJSONString(request)); -// HttpRequestOptions postConfig = HttpRequestOptions.create() -// .url(notificationProxyUrl + url) -// .timeout(requestConfig) -// .json(JSON.toJSONString(request)) -// .method(HttpMethods.POST) -// .client(httpClient); -// HttpResult result = HttpClientExecutor.sendAndPostResp(postConfig, headers); -// log.info("POST request result={}", result); -// return result; -// } } diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/services/DaylightApiService.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/services/DaylightApiService.java index 4fba1ac..c92b572 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/daylight/services/DaylightApiService.java +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/services/DaylightApiService.java @@ -3,7 +3,7 @@ package com.mgm.mcrm.apigateway.daylight.services; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import com.mgm.mcrm.apigateway.common.dtos.BaseApiResponse; -import com.mgm.mcrm.apigateway.common.httpclient.HttpHeaderBuilder; +import com.mgm.mcrm.apigateway.daylight.common.httpclient.HttpHeaderBuilder; import com.mgm.mcrm.apigateway.daylight.common.services.HttpService; import com.mgm.mcrm.apigateway.daylight.dtos.response.CompRuleResponse; import jakarta.annotation.Resource; diff --git a/src/main/java/com/mgm/mcrm/apigateway/esb/common/dtos/mgmapi/PlayerProfileApiResult.java b/src/main/java/com/mgm/mcrm/apigateway/esb/common/dtos/mgmapi/PlayerProfileApiResult.java index 4d0d069..fad8445 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/esb/common/dtos/mgmapi/PlayerProfileApiResult.java +++ b/src/main/java/com/mgm/mcrm/apigateway/esb/common/dtos/mgmapi/PlayerProfileApiResult.java @@ -24,7 +24,7 @@ public class PlayerProfileApiResult { private String state; private String passport = null; private String language = null; - private List nameIds=new ArrayList(); + private List nameIds=new ArrayList<>(); public PlayerProfileApiResult() { diff --git a/src/main/java/com/mgm/mcrm/apigateway/esb/common/services/EsbTokenService.java b/src/main/java/com/mgm/mcrm/apigateway/esb/common/services/EsbTokenService.java index 2a0f62b..4d30051 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/esb/common/services/EsbTokenService.java +++ b/src/main/java/com/mgm/mcrm/apigateway/esb/common/services/EsbTokenService.java @@ -13,6 +13,7 @@ import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; @@ -21,6 +22,8 @@ import org.springframework.stereotype.Service; import java.nio.charset.StandardCharsets; import java.time.Duration; import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; @Service @@ -31,21 +34,26 @@ public class EsbTokenService { private WebProxyService webProxyService; @Autowired private EsbApiSetting esbApiSetting; - private EsbToken cachedToken = new EsbToken(); + @Value("${mcrm.proxy.api-config.esb.api-path.mcrm}") + private String mcrmApiPath; + @Value("${mcrm.proxy.api-config.esb.api-path.limo}") + private String limoApiPath; + private final Map cachedTokens = new HashMap<>(); // 重试间隔时间:1秒 - private final long RETRY_DELAY_MS = 1000; + private static final long RETRY_DELAY_MS = 1000; - public EsbToken getApiAccessToken() throws Exception { + public synchronized EsbToken getApiAccessToken(String apiType) throws Exception { EsbToken returnToken = null; int retryCnt=0; boolean isNeedTry=true; String lastErrorMsg =""; + EsbToken cachedToken = cachedTokens.get(apiType); if (!isTokenExpire(cachedToken)) { return cachedToken; }else{ while(returnToken==null && isNeedTry && retryCnt<3){ - HttpResult requestResult = this.invokeTokenRequest(); + HttpResult requestResult = this.invokeTokenRequest(apiType); if(requestResult.getStatusCode()>=200 && requestResult.getStatusCode()<300){ try{ EsbToken newToken = JSON.parseObject(requestResult.getResponseBody(), EsbToken.class); @@ -59,6 +67,7 @@ public class EsbTokenService { expiryTime = LocalDateTime.now().plusSeconds(esbApiSetting.getTokenExpireSecond()); newToken.setExpireTime(expiryTime); } + cachedTokens.put(apiType, newToken); returnToken = newToken; } }catch(Exception err){ @@ -98,10 +107,15 @@ public class EsbTokenService { return headers; } - private byte[] buildTokenRequestBody() { + private byte[] buildTokenRequestBody(String apiType) { JSONObject requestObj = new JSONObject(); - requestObj.put("AppId", esbApiSetting.getAppId()); - requestObj.put("AppSecret", esbApiSetting.getAppSecret()); + if("ESB_LIMO".equalsIgnoreCase(apiType)){ + requestObj.put("AppId", esbApiSetting.getAppIdLimo()); + requestObj.put("AppSecret", esbApiSetting.getAppSecretLimo()); + }else{ + requestObj.put("AppId", esbApiSetting.getAppIdMcrm()); + requestObj.put("AppSecret", esbApiSetting.getAppSecretMcrm()); + } return requestObj.toJSONString().getBytes(StandardCharsets.UTF_8); } /** @@ -113,11 +127,12 @@ public class EsbTokenService { /** * 发送 token 请求(单次),返回详细结果包括状态码 */ - private HttpResult invokeTokenRequest() { + private HttpResult invokeTokenRequest(String apiType) { HttpResult result = new HttpResult(); - String tokenUrl =esbApiSetting.getServerUrl() + esbApiSetting.getTokenUrl(); + String apiPath = "ESB_LIMO".equalsIgnoreCase(apiType) ? limoApiPath : mcrmApiPath; + String tokenUrl = esbApiSetting.getServerUrl() + apiPath + esbApiSetting.getTokenUrl(); try { - byte[] requestBody = buildTokenRequestBody(); + byte[] requestBody = buildTokenRequestBody(apiType); HttpHeaders headers = buildHeaders(); // 构建完整的 URL ResponseEntity requestRes = webProxyService.invokeRequest( diff --git a/src/main/java/com/mgm/mcrm/apigateway/esb/common/services/MGMApiService.java b/src/main/java/com/mgm/mcrm/apigateway/esb/common/services/MGMApiService.java index 4d6e0b3..e94baa4 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/esb/common/services/MGMApiService.java +++ b/src/main/java/com/mgm/mcrm/apigateway/esb/common/services/MGMApiService.java @@ -32,7 +32,7 @@ public class MGMApiService { public HttpResult invokeGetAllPlayerProfile(String playerId) throws Exception { HttpResult result = new HttpResult(); String apiLink = esbApiSetting.getServerUrl() + API_GET_ALL_PLAYER_PROFILE; - EsbToken esbToken = esbTokenService.getApiAccessToken(); + EsbToken esbToken = esbTokenService.getApiAccessToken("ESB_MCRM"); if(esbToken==null || StringUtils.isBlank(esbToken.getAccessToken())){ throw new Exception("Invoke ESB token failed"); } @@ -74,7 +74,7 @@ public class MGMApiService { public HttpResult invokeGetUserInfo(String userId,String userName) throws Exception { HttpResult result = new HttpResult();; String apiLink = esbApiSetting.getServerUrl() + API_GET_USER_INFO; - EsbToken esbToken = esbTokenService.getApiAccessToken(); + EsbToken esbToken = esbTokenService.getApiAccessToken("ESB_MCRM"); if(esbToken==null || StringUtils.isBlank(esbToken.getAccessToken())){ throw new Exception("Invoke ESB token failed"); } diff --git a/src/main/java/com/mgm/mcrm/apigateway/esb/player/services/MgmUserService.java b/src/main/java/com/mgm/mcrm/apigateway/esb/player/services/MgmUserService.java index 3f71baf..d1e6fc9 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/esb/player/services/MgmUserService.java +++ b/src/main/java/com/mgm/mcrm/apigateway/esb/player/services/MgmUserService.java @@ -63,7 +63,7 @@ public class MgmUserService { if("OK".equals(resultCode)) { List recrods = resultObj.getRecords(); if(!recrods.isEmpty()) { - UserInfoApiRecord user = recrods.get(0); + UserInfoApiRecord user = recrods.getFirst(); if(!"NULL".equals(user.getUserId())) { user = this.supplementInfoId(userId,user); user.setStatusCode(resultObj.getStatusCode()); diff --git a/src/main/java/com/mgm/mcrm/apigateway/logging/filters/ApiGatewayLoggingFilter.java b/src/main/java/com/mgm/mcrm/apigateway/logging/filters/ApiGatewayLoggingFilter.java index 6f92154..660d87e 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/logging/filters/ApiGatewayLoggingFilter.java +++ b/src/main/java/com/mgm/mcrm/apigateway/logging/filters/ApiGatewayLoggingFilter.java @@ -47,7 +47,7 @@ public class ApiGatewayLoggingFilter extends OncePerRequestFilter { ) throws IOException, ServletException { Throwable handleException = null; - ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request); + ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request,1024 * 1024); ContentCachingResponseWrapper wrappedResponse = new ContentCachingResponseWrapper(response); LocalDateTime startTime = LocalDateTime.now(); try { diff --git a/src/main/java/com/mgm/mcrm/apigateway/logging/filters/WebhookLoggingFilter.java b/src/main/java/com/mgm/mcrm/apigateway/logging/filters/WebhookLoggingFilter.java index 48ebdf3..76af400 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/logging/filters/WebhookLoggingFilter.java +++ b/src/main/java/com/mgm/mcrm/apigateway/logging/filters/WebhookLoggingFilter.java @@ -35,7 +35,7 @@ public class WebhookLoggingFilter extends OncePerRequestFilter { ) throws IOException, ServletException { Throwable handleException = null; - ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request); + ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request,1024 * 1024); ContentCachingResponseWrapper wrappedResponse = new ContentCachingResponseWrapper(response); LocalDateTime startTime = LocalDateTime.now(); try { diff --git a/src/main/java/com/mgm/mcrm/apigateway/notification/controllers/NotificationController.java b/src/main/java/com/mgm/mcrm/apigateway/notification/controllers/NotificationController.java index c1bacf5..4634c9f 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/notification/controllers/NotificationController.java +++ b/src/main/java/com/mgm/mcrm/apigateway/notification/controllers/NotificationController.java @@ -31,10 +31,6 @@ public class NotificationController { @RequestBody(required = true) SendEmailRequest requestData, HttpServletRequest request ){ - - String appId = request.getHeader("application-id"); - String securityKey = request.getHeader("authorization"); - BaseApiResponse result = emailNotificationService.sendEmailByRequest(requestData,emailProxySetting); return ResponseEntity.status(HttpStatus.OK).body(result); } diff --git a/src/main/java/com/mgm/mcrm/apigateway/proxy/config/WebClientConfig.java b/src/main/java/com/mgm/mcrm/apigateway/proxy/config/WebClientConfig.java index c0bf39c..d9ebcf7 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/proxy/config/WebClientConfig.java +++ b/src/main/java/com/mgm/mcrm/apigateway/proxy/config/WebClientConfig.java @@ -1,12 +1,14 @@ package com.mgm.mcrm.apigateway.proxy.config; +import com.mgm.mcrm.apigateway.common.constants.SysCommon; import com.mgm.mcrm.apigateway.common.dtos.config.DaylightApiSetting; import com.mgm.mcrm.apigateway.common.dtos.config.EsbApiSetting; +import com.mgm.mcrm.apigateway.common.dtos.config.LimoApiSetting; import com.mgm.mcrm.apigateway.common.dtos.config.OperaApiSetting; +import com.mgm.mcrm.apigateway.common.services.ConfigurableTrustHandler; import io.netty.channel.ChannelOption; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.timeout.ReadTimeoutHandler; import io.netty.handler.timeout.WriteTimeoutHandler; import org.apache.commons.lang3.StringUtils; @@ -52,6 +54,8 @@ public class WebClientConfig { @Autowired private OperaApiSetting operaApiSetting; @Autowired + private LimoApiSetting limoApiSetting; + @Autowired private SslBundles sslBundles; private final Logger logger = LoggerFactory.getLogger(WebClientConfig.class); @@ -136,6 +140,45 @@ public class WebClientConfig { .build(); } + @Bean + public WebClient limoWebClient() { + ConnectionProvider provider = ConnectionProvider.builder("proxy-pool") + .maxConnections(maxConnectionPool) + .pendingAcquireTimeout(Duration.ofSeconds(60)) + .maxIdleTime(Duration.ofSeconds(30)) + .build(); + + HttpClient httpClient = HttpClient.create(provider) + .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectTimeout*1000) + .responseTimeout(Duration.ofSeconds(readTimeout)) + .secure(spec -> { + try { + spec.sslContext(this.createLimoSslContent()); + } catch (Exception e) { + throw new RuntimeException(e); + } + }) + .doOnConnected(conn -> { + logger.info("LIMO Connected using proxy: {}", conn.channel().remoteAddress()); + conn.addHandlerLast(new ReadTimeoutHandler(readTimeout, TimeUnit.SECONDS)) + .addHandlerLast(new WriteTimeoutHandler(readTimeout, TimeUnit.SECONDS)); + }) + .doOnRequest((request, connection) -> + logger.info("LIMO Request via proxy: {}", request) + ); + if(StringUtils.isNotBlank(limoApiSetting.getProxyHost())){ + httpClient = httpClient.proxy(this::initLimoProxy); + } + + return WebClient.builder() + .clientConnector(new ReactorClientHttpConnector(httpClient)) + .codecs(configurer -> { + configurer.defaultCodecs().maxInMemorySize(maxMemoryCacheM * 1024 * 1024); + }) + .filter(retryFilter()) + .build(); + } + @Bean public WebClient operaWebClient() { ConnectionProvider provider = ConnectionProvider.builder("proxy-pool") @@ -190,10 +233,10 @@ public class WebClientConfig { } //-- Daylight init private SslContext createDaylightSslContent() throws SSLException { - if(StringUtils.isBlank(daylightApiSetting.getSslBundleName())){ - return this.createNoVerifySslContext(); - }else{ + if(StringUtils.isNotBlank(daylightApiSetting.getSslBundleName())){ return this.createSslContextByConfig(daylightApiSetting.getSslBundleName()); + }else{ + return this.createDefaultSslContext(new ConfigurableTrustHandler(daylightApiSetting.isSkipTrustValidation(), SysCommon.getDefaultTrustManager())); } } @@ -208,10 +251,10 @@ public class WebClientConfig { } //-- Opera init private SslContext createOperaSslContent() throws SSLException { - if(StringUtils.isBlank(operaApiSetting.getSslBundleName())){ - return this.createNoVerifySslContext(); - }else{ + if(StringUtils.isNotBlank(operaApiSetting.getSslBundleName())){ return this.createSslContextByConfig(operaApiSetting.getSslBundleName()); + }else{ + return this.createDefaultSslContext(new ConfigurableTrustHandler(operaApiSetting.isSkipTrustValidation(), SysCommon.getDefaultTrustManager())); } } @@ -226,10 +269,10 @@ public class WebClientConfig { } //-- Esb init private SslContext createEsbSslContent() throws SSLException { - if(StringUtils.isBlank(esbApiSetting.getSslBundleName())){ - return this.createNoVerifySslContext(); - }else{ + if(StringUtils.isNotBlank(esbApiSetting.getSslBundleName())){ return this.createSslContextByConfig(esbApiSetting.getSslBundleName()); + }else{ + return this.createDefaultSslContext(new ConfigurableTrustHandler(esbApiSetting.isSkipTrustValidation(),SysCommon.getDefaultTrustManager())); } } @@ -243,6 +286,27 @@ public class WebClientConfig { ); } + //-- Limo init + private SslContext createLimoSslContent() throws SSLException { + if(StringUtils.isNotBlank(limoApiSetting.getSslBundleName())){ + return this.createSslContextByConfig(limoApiSetting.getSslBundleName()); + }else{ + return this.createDefaultSslContext(new ConfigurableTrustHandler(limoApiSetting.isSkipTrustValidation(),SysCommon.getDefaultTrustManager())); + } + } + + private void initLimoProxy(ProxyProvider.TypeSpec proxy){ + this.setProxySettingByConfig( + proxy, + limoApiSetting.getProxyHost(), + limoApiSetting.getProxyPort(), + limoApiSetting.getProxyUserName(), + limoApiSetting.getProxyPassword() + ); + } + + + //---------- private void setProxySettingByConfig(ProxyProvider.TypeSpec proxy, String proxyHost, int proxyPort, String authUserName, String authPassword){ if (StringUtils.isNotBlank(authUserName) && StringUtils.isNotBlank(authPassword)) { @@ -258,9 +322,8 @@ public class WebClientConfig { } } - private SslContext createNoVerifySslContext() throws SSLException { - return SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE).build(); + private SslContext createDefaultSslContext(ConfigurableTrustHandler trustHandler) throws SSLException { + return SslContextBuilder.forClient().trustManager(trustHandler).build(); } private SslContext createSslContextByConfig(String sslBundleName) throws SSLException { diff --git a/src/main/java/com/mgm/mcrm/apigateway/proxy/controllers/NotificationProxyController.java b/src/main/java/com/mgm/mcrm/apigateway/proxy/controllers/NotificationProxyController.java index 6dd5910..a1e4b7e 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/proxy/controllers/NotificationProxyController.java +++ b/src/main/java/com/mgm/mcrm/apigateway/proxy/controllers/NotificationProxyController.java @@ -1,36 +1,23 @@ package com.mgm.mcrm.apigateway.proxy.controllers; -import com.mgm.mcrm.apigateway.common.constants.SysCommon; import com.mgm.mcrm.apigateway.common.dtos.BaseApiResponse; -import com.mgm.mcrm.apigateway.common.dtos.LoginUserInfo; -import com.mgm.mcrm.apigateway.common.dtos.config.DaylightApiSetting; -import com.mgm.mcrm.apigateway.common.dtos.config.EsbApiSetting; -import com.mgm.mcrm.apigateway.common.dtos.config.OperaApiSetting; -import com.mgm.mcrm.apigateway.daylight.common.services.DaylightTokenService; -import com.mgm.mcrm.apigateway.esb.common.services.EsbTokenService; +import com.mgm.mcrm.apigateway.common.dtos.config.EmailProxySetting; import com.mgm.mcrm.apigateway.proxy.dtos.request.SendNotificationRequest; -import com.mgm.mcrm.apigateway.proxy.exception.HttpProxyErrorHandler; -import com.mgm.mcrm.apigateway.proxy.exception.SoapProxyErrorHandler; import com.mgm.mcrm.apigateway.proxy.services.NotificationProxyService; -import com.mgm.mcrm.apigateway.proxy.services.WebProxyService; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import java.io.IOException; -import java.time.Duration; -import java.util.Collections; - @Tag(name = "Notification Proxy", description = "Controller for Notification proxy") @RestController public class NotificationProxyController { @@ -39,32 +26,29 @@ public class NotificationProxyController { @Resource private NotificationProxyService notificationProxyService; - @RequestMapping("/api/notification/proxy/**") + @Autowired + private EmailProxySetting emailProxySetting; + + @RequestMapping(value="/api/notification/proxy/**",produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> invokeNotificationApiProxy(HttpServletRequest request, @RequestBody SendNotificationRequest notificationRequest) throws Exception { - LoginUserInfo requestUser = SysCommon.parseUserFromToken(request.getHeader("Authorization")); - return ResponseEntity.status(HttpStatus.OK).body(notificationProxyService.sendNotificationByRequest(notificationRequest,requestUser.getLoginId())); - } - - private HttpHeaders extractHeaders(HttpServletRequest request) { - HttpHeaders headers = new HttpHeaders(); - Collections.list(request.getHeaderNames()).forEach(name -> { - if (!name.equalsIgnoreCase("host") && - !name.equalsIgnoreCase("content-length") && - !name.equalsIgnoreCase("application-id") && - !name.equalsIgnoreCase("authorization") - ) { - headers.add(name, request.getHeader(name)); - } - }); - return headers; - } - - private byte[] getRequestBody(HttpServletRequest request) { - try { - return request.getInputStream().readAllBytes(); - } catch (IOException e) { - logger.error("Failed to read request body of request[{}]",request.getRequestURI(), e); - return new byte[0]; + //--Check authorization + String appId = request.getHeader("application-id"); + String securityKey = request.getHeader("authorization"); + if(!isAuthorizated(appId, securityKey)){ + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null); } + return ResponseEntity.status(HttpStatus.OK).body(notificationProxyService.sendNotificationByRequest(notificationRequest,notificationRequest.getRequestorId())); } + + private boolean isAuthorizated(String appId, String securityKey) { + boolean isAuth=false; + if(StringUtils.isNotBlank(appId) && StringUtils.isNotBlank(securityKey)){ + String authCode = emailProxySetting.getAppAuthorizatinKey(appId); + if(StringUtils.isNotBlank(authCode) && securityKey.equals(authCode)){ + isAuth=true; + } + } + return isAuth; + } + } diff --git a/src/main/java/com/mgm/mcrm/apigateway/proxy/controllers/WebProxyController.java b/src/main/java/com/mgm/mcrm/apigateway/proxy/controllers/WebProxyController.java index fa00ce0..2c18368 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/proxy/controllers/WebProxyController.java +++ b/src/main/java/com/mgm/mcrm/apigateway/proxy/controllers/WebProxyController.java @@ -3,7 +3,9 @@ package com.mgm.mcrm.apigateway.proxy.controllers; import com.mgm.mcrm.apigateway.appsetting.services.SysSettingService; import com.mgm.mcrm.apigateway.common.dtos.config.DaylightApiSetting; import com.mgm.mcrm.apigateway.common.dtos.config.EsbApiSetting; +import com.mgm.mcrm.apigateway.common.dtos.config.LimoApiSetting; import com.mgm.mcrm.apigateway.common.dtos.config.OperaApiSetting; +import com.mgm.mcrm.apigateway.common.utils.NetworkUtil; import com.mgm.mcrm.apigateway.daylight.common.services.DaylightTokenService; import com.mgm.mcrm.apigateway.daylight.common.dtos.TokenResponse; import com.mgm.mcrm.apigateway.esb.common.dtos.EsbToken; @@ -16,11 +18,14 @@ import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -50,6 +55,12 @@ public class WebProxyController { private DaylightApiSetting daylightApiSetting; @Autowired private EsbApiSetting esbApiSetting; + @Value("${mcrm.proxy.api-config.esb.api-path.mcrm}") + private String mcrmApiPath; + @Value("${mcrm.proxy.api-config.esb.api-path.limo}") + private String limoApiPath; + @Autowired + private LimoApiSetting limoApiSetting; @Autowired private OperaApiSetting operaApiSetting; @@ -59,10 +70,21 @@ public class WebProxyController { private volatile boolean cachedIsMock = true; private volatile long lastMockCheckTime = 0; private static final long MOCK_CHECK_INTERVAL_MS = 30000; + private static final String DAYLIGHT_PROXY_TYPE="DAYLIGHT"; + private static final String ESB_PROXY_TYPE="ESB"; + private static final String LIMO_PROXY_TYPE="LIMO"; @RequestMapping("/api/daylight/proxy/**") public ResponseEntity invokeDaylightApiProxy(HttpServletRequest request,HttpServletResponse response) throws Exception { + //--Check authorization + String appId = request.getHeader("application-id"); + String securityKey = request.getHeader("authorization"); + if(!isAuthorizated(DAYLIGHT_PROXY_TYPE, appId, securityKey)){ + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null); + } + String apiUrl = daylightApiSetting.getServerUrl() + request.getRequestURI().replace("/api/daylight/proxy", ""); + apiUrl = NetworkUtil.validateAndSanitizeUrl(apiUrl); HttpHeaders headers = this.extractHeaders(request); TokenResponse daylightApiToken = daylightTokenService.getApiAccessToken(); if(daylightApiToken==null || StringUtils.isBlank(daylightApiToken.getAccessToken())){ @@ -74,7 +96,7 @@ public class WebProxyController { WebProxyService.DAYLIGHT_API, HttpMethod.valueOf(request.getMethod()), apiUrl, - request.getQueryString(), + NetworkUtil.sanitizeCRLF(request.getQueryString()), headers, getRequestBody(request), Duration.ofSeconds(daylightApiSetting.getApiTimeout()), @@ -83,15 +105,40 @@ public class WebProxyController { } @RequestMapping("/api/esb/proxy/**") public ResponseEntity invokeEsbApiProxy(HttpServletRequest request) throws Exception { + return this.processEsbRequest(request, "ESB_MCRM"); + } + + @RequestMapping("/api/esb_limo/proxy/**") + public ResponseEntity invokeEsbLimoApiProxy(HttpServletRequest request) throws Exception { + return this.processEsbRequest(request, "ESB_LIMO"); + } + + private ResponseEntity processEsbRequest(HttpServletRequest request, String apiType) throws Exception { + //--Check authorization + String appId = request.getHeader("application-id"); + String securityKey = request.getHeader("authorization"); + if(!isAuthorizated(ESB_PROXY_TYPE, appId, securityKey)){ + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null); + } + HttpMethod method = HttpMethod.valueOf(request.getMethod()); - String apiUrl = esbApiSetting.getServerUrl() + request.getRequestURI().replace("/api/esb/proxy", ""); - String queryString = request.getQueryString(); + boolean isLimo = "ESB_LIMO".equalsIgnoreCase(apiType); + String apiPath = isLimo ? limoApiPath : mcrmApiPath; + String proxyPath = isLimo ? "/api/esb_limo/proxy" : "/api/esb/proxy"; + String requestPath = Strings.CI.removeStart(request.getRequestURI(), proxyPath); + if (isLimo) { + requestPath = Strings.CI.removeStart(requestPath, "/api/esb_limo/v1.0"); + } + requestPath = Strings.CI.removeStart(requestPath, apiPath); + String apiUrl = esbApiSetting.getServerUrl() + apiPath + requestPath; + apiUrl = NetworkUtil.validateAndSanitizeUrl(apiUrl); + String queryString = NetworkUtil.sanitizeCRLF(request.getQueryString()); HttpHeaders headers = this.extractHeaders(request); byte[] requestBody = getRequestBody(request); - EsbToken esbToken = esbTokenService.getApiAccessToken(); + EsbToken esbToken = esbTokenService.getApiAccessToken(apiType); if(esbToken==null || StringUtils.isBlank(esbToken.getAccessToken())){ - logger.error("invokeEsbApiProxy failed, ESB token is null or blank, uri={}", request.getRequestURI()); + logger.error("invoke {} Proxy failed, ESB token is null or blank, uri={}", apiType, request.getRequestURI()); throw new Exception("Invoke ESB token failed"); } headers.set("authorization", esbToken.getAccessToken()); @@ -107,11 +154,11 @@ public class WebProxyController { httpErrorHandler ).block(); if (response == null) { - logger.error("invokeEsbApiProxy received a null response: method={}, apiUrl={}, query={}", + logger.error("invoke Esb ApiProxy received a null response: method={}, apiUrl={}, query={}", method, apiUrl, queryString); } else if (response.getStatusCode().isError()) { byte[] responseBody = response.getBody(); - logger.warn("invokeEsbApiProxy error response: method={}, apiUrl={}, query={}, status={}, headers={}, bodyLength={}, bodyPreview={}", + logger.warn("invoke Esb Api Proxy error response: method={}, apiUrl={}, query={}, status={}, headers={}, bodyLength={}, bodyPreview={}", method, apiUrl, queryString, @@ -121,7 +168,54 @@ public class WebProxyController { responseBody == null ? 0 : responseBody.length, bodyPreview(responseBody)); } else { - logger.info("invokeEsbApiProxy completed: method={}, apiUrl={}, query={}, status={}", + logger.info("invoke Esb Api Proxy completed: method={}, apiUrl={}, query={}, status={}", + method, apiUrl, queryString, response.getStatusCode()); + } + return response; + } + + @RequestMapping("/api/limo/proxy/**") + public ResponseEntity invokeLimoApiProxy(HttpServletRequest request) throws Exception { + //--Check authorization + String appId = request.getHeader("application-id"); + String securityKey = request.getHeader("authorization"); + if(!isAuthorizated(LIMO_PROXY_TYPE, appId, securityKey)){ + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null); + } + + HttpMethod method = HttpMethod.valueOf(request.getMethod()); + String apiUrl = limoApiSetting.getServerUrl() + request.getRequestURI().replace("/api/limo/proxy", ""); + apiUrl = NetworkUtil.validateAndSanitizeUrl(apiUrl); + String queryString = NetworkUtil.sanitizeCRLF(request.getQueryString()); + HttpHeaders headers = this.extractHeaders(request); + byte[] requestBody = getRequestBody(request); + + ResponseEntity response = webProxyService.invokeRequest( + WebProxyService.LIMO_API, + method, + apiUrl, + queryString, + headers, + requestBody, + Duration.ofSeconds(limoApiSetting.getApiRequestTimeout()), + httpErrorHandler + ).block(); + if (response == null) { + logger.error("invoke Limo Api Proxy received a null response: method={}, apiUrl={}, query={}", + method, apiUrl, queryString); + } else if (response.getStatusCode().isError()) { + byte[] responseBody = response.getBody(); + logger.warn("invoke Limo Api Proxy error response: method={}, apiUrl={}, query={}, status={}, headers={}, bodyLength={}, bodyPreview={}", + method, + apiUrl, + queryString, + + response.getStatusCode(), + sanitizeHeaders(response.getHeaders()), + responseBody == null ? 0 : responseBody.length, + bodyPreview(responseBody)); + } else { + logger.info("invoke Limo Api Proxy completed: method={}, apiUrl={}, query={}, status={}", method, apiUrl, queryString, response.getStatusCode()); } return response; @@ -130,11 +224,12 @@ public class WebProxyController { @RequestMapping("/api/opera/proxy/**") public ResponseEntity invokeOperaApiProxy(HttpServletRequest request) throws Exception { String apiUrl = operaApiSetting.getServerUrl() + request.getRequestURI().replace("/api/opera/proxy", ""); + apiUrl = NetworkUtil.validateAndSanitizeUrl(apiUrl); return webProxyService.invokeRequest( WebProxyService.OPERA_API, HttpMethod.valueOf(request.getMethod()), apiUrl, - request.getQueryString(), + NetworkUtil.sanitizeCRLF(request.getQueryString()), extractHeaders(request), getRequestBody(request), Duration.ofSeconds(operaApiSetting.getTimeout()), @@ -142,6 +237,24 @@ public class WebProxyController { ).block(); } + private boolean isAuthorizated(String proxyType, String appId, String securityKey) { + boolean isAuth=false; + if(StringUtils.isNotBlank(appId) && StringUtils.isNotBlank(securityKey)){ + String authCode = null; + if(ESB_PROXY_TYPE.equalsIgnoreCase(proxyType)){ + authCode = esbApiSetting.getAppAuthorizatinKey(appId); + }else if(DAYLIGHT_PROXY_TYPE.equalsIgnoreCase(proxyType)){ + authCode = daylightApiSetting.getAppAuthorizatinKey(appId); + }else if(LIMO_PROXY_TYPE.equalsIgnoreCase(proxyType)){ + authCode = limoApiSetting.getAppAuthorizatinKey(appId); + } + if(StringUtils.isNotBlank(authCode) && securityKey.equals(authCode)){ + isAuth=true; + } + } + return isAuth; + } + private HttpHeaders extractHeaders(HttpServletRequest request) { HttpHeaders headers = new HttpHeaders(); Collections.list(request.getHeaderNames()).forEach(name -> { @@ -150,7 +263,7 @@ public class WebProxyController { !name.equalsIgnoreCase("application-id") && !name.equalsIgnoreCase("authorization") ) { - headers.add(name, request.getHeader(name)); + headers.add(NetworkUtil.sanitizeCRLF(name), NetworkUtil.sanitizeCRLF(request.getHeader(name))); } }); return headers; diff --git a/src/main/java/com/mgm/mcrm/apigateway/proxy/dtos/request/SendNotificationRequest.java b/src/main/java/com/mgm/mcrm/apigateway/proxy/dtos/request/SendNotificationRequest.java index 59b27d6..4950409 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/proxy/dtos/request/SendNotificationRequest.java +++ b/src/main/java/com/mgm/mcrm/apigateway/proxy/dtos/request/SendNotificationRequest.java @@ -26,4 +26,6 @@ public class SendNotificationRequest { @Schema(description = "Type of booking ") private String type; + + private String requestorId; } diff --git a/src/main/java/com/mgm/mcrm/apigateway/proxy/services/WebProxyService.java b/src/main/java/com/mgm/mcrm/apigateway/proxy/services/WebProxyService.java index a83da35..809ea2d 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/proxy/services/WebProxyService.java +++ b/src/main/java/com/mgm/mcrm/apigateway/proxy/services/WebProxyService.java @@ -1,8 +1,7 @@ package com.mgm.mcrm.apigateway.proxy.services; -import com.mgm.mcrm.apigateway.common.utils.LogUtil; +import com.mgm.mcrm.apigateway.common.utils.NetworkUtil; import com.mgm.mcrm.apigateway.common.utils.mockup.HttpUtilMockup; -import com.mgm.mcrm.apigateway.proxy.exception.HttpProxyErrorHandler; import com.mgm.mcrm.apigateway.proxy.exception.ProxyErrorHandler; import lombok.RequiredArgsConstructor; import org.slf4j.Logger; @@ -23,7 +22,6 @@ import reactor.core.publisher.Mono; import java.net.URI; import java.nio.charset.StandardCharsets; import java.time.Duration; -import java.util.concurrent.ForkJoinPool; @RequiredArgsConstructor @Service @@ -31,9 +29,11 @@ public class WebProxyService { public final static String DAYLIGHT_API="DAYLIGHT_API"; public final static String ESB_API="ESB_API"; public final static String OPERA_API="OPERA_API"; + public final static String LIMO_API="LIMO_API"; private final WebClient daylightWebClient; private final WebClient esbWebClient; private final WebClient operaWebClient; + private final WebClient limoWebClient; private final Logger logger = LoggerFactory.getLogger(WebProxyService.class); public Mono> invokeRequest( @@ -54,6 +54,8 @@ public class WebProxyService { targetWebClient=esbWebClient; }else if(OPERA_API.equalsIgnoreCase(apiType)){ targetWebClient=operaWebClient; + }else if(LIMO_API.equalsIgnoreCase(apiType)){ + targetWebClient=limoWebClient; }else{ throw new Exception("Unknown request API type of "+apiType); } diff --git a/src/main/java/com/mgm/mcrm/apigateway/reservation/services/BookingService.java b/src/main/java/com/mgm/mcrm/apigateway/reservation/services/BookingService.java index a34cfb6..473f9fc 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/reservation/services/BookingService.java +++ b/src/main/java/com/mgm/mcrm/apigateway/reservation/services/BookingService.java @@ -139,88 +139,88 @@ public class BookingService { } bookingDao.save(dbBooking); - CfgProperties property = propertyDao.findById(dbBooking.getPropertyId()).orElse(null); +// CfgProperties property = propertyDao.findById(dbBooking.getPropertyId()).orElse(null); - //Send Notification to Booker when booking lastOpera status Changed - if (Strings.CS.equals(daylightBooking.getLastOperaStatus(),"RESERVED") && !Strings.CS.equals(dbBookingStatus,daylightBooking.getLastOperaStatus())){ - LogUtil.info(BookingService.class,"Booking last opera status changed, DB Record status:"+dbBookingStatus+", DlBooking status:"+daylightBooking.getLastOperaStatus()); - //设置Notification 内容 - String notificationTitle = "#EN# Room status updated#EN#" + - "#CN#房間狀態已更改#CN#"; - String notificationContent = "#EN#Player {playerId}, {propertyName}, {roomCode},room status changed to reserved.#EN#" + - "#CN#Player {playerId}, {propertyName}, {roomCode}, 房間狀態已預訂。#CN#"; - String[] placeholders = { - "{playerId}", "{propertyName}", "{roomCode}" - }; - LogUtil.info(BookingService.class,"Set notification info"); - String[] values = { - StringUtils.defaultString(dbBooking.getPlayerId()), - StringUtils.defaultString(property == null ? null : property.getName()), - StringUtils.defaultString(dbBooking.getRoomTypeCode()) - }; - LogUtil.info(BookingService.class,"Replace each"); - notificationContent = StringUtils.replaceEach(notificationContent, placeholders, values); - String transactionId = "DLBKSC"+dbBooking.getId(); - LogUtil.info(BookingService.class,"Get device token from DB Start"); - LogUtil.info(BookingService.class,"dbBooking.getCreatedBy()=" + dbBooking.getCreatedBy()); - SysLogDeviceToken deviceToken = sysLogDeviceTokenRepository.findByUserId(StringUtils.trim(dbBooking.getCreatedBy())); - LogUtil.info(BookingService.class,"Get device token from DB:" + (deviceToken == null ? "null" : deviceToken.getDeviceToken())); - String deviceTokenValue =null; - String finalTitle = null; - String finalContent = null; - if(deviceToken!=null){ - deviceTokenValue = deviceToken.getDeviceToken(); - finalTitle = notificationUtil.distinguishLanguage(notificationTitle,deviceToken.getUseLanguageType()); - notificationContent = notificationUtil.distinguishLanguage(notificationContent,deviceToken.getUseLanguageType()); - - } -// NOTIFICATION - apnNotificationService.pushApnMessage(transactionId,deviceTokenValue,finalTitle,notificationContent,true); - } - - LogUtil.info(BookingService.class,"Daylight webhook updated, prepare send notification"); - log.info("dbBooking checkin={}, checkout={}", checkInDate, checkOutDate); - log.info("dlBooking checkin={}, checkout={}", daylightBooking.getCheckinDate(), daylightBooking.getCheckoutDate()); - LogUtil.info(BookingService.class,"Db booking status=" + dbBookingStatus); - //Send notification to Booker when Booking C/I | C/O date changed - if ((checkInDate != null && !checkInDate.equals(daylightBooking.getCheckinDate()))|| - (checkOutDate != null && !checkOutDate.equals(daylightBooking.getCheckoutDate()))) { - LogUtil.info(BookingService.class,"Booking date changed, DB Record C/I:"+checkInDate+", DB Record C/O:"+checkOutDate+ - ", Dl Record C/I"+daylightBooking.getCheckinDate()+", Dl Record C/O"+daylightBooking.getCheckoutDate()); - - - //设置Notification 内容 - String notificationTitle = "#EN# Room status updated#EN#" + - "#CN#房間狀態已更改#CN#"; - String notificationContent = "#EN#Player {playerId}, {propertyName}, {roomCode},Stay date changed.#EN#" + - "#CN#Player {playerId}, {propertyName}, {roomCode}, 住宿日期已變更。#CN#"; - String[] placeholders = { - "{playerId}", "{propertyName}", "{roomCode}" - }; - String[] values = { - StringUtils.defaultString(dbBooking.getPlayerId()), - StringUtils.defaultString(property == null ? null : property.getName()), - StringUtils.defaultString(dbBooking.getRoomTypeCode()) - }; - - notificationContent = StringUtils.replaceEach(notificationContent, placeholders, values); - String transactionId = "DLBKDC" + dbBooking.getId(); - - SysLogDeviceToken deviceToken = sysLogDeviceTokenRepository.findByUserId(StringUtils.trim(dbBooking.getCreatedBy())); - LogUtil.info(BookingService.class,"Get device token from DB:" + (deviceToken == null ? "null" : deviceToken.getDeviceToken())); - String deviceTokenValue = null; - String finalTitle = null; - String finalContent = null; - if (deviceToken != null) { - LogUtil.info(BookingService.class,"Get device Token success"); - deviceTokenValue = deviceToken.getDeviceToken(); - finalTitle = notificationUtil.distinguishLanguage(notificationTitle, deviceToken.getUseLanguageType()); - notificationContent = notificationUtil.distinguishLanguage(notificationContent, deviceToken.getUseLanguageType()); - } -// NOTIFICATION - apnNotificationService.pushApnMessage(transactionId, deviceTokenValue, finalTitle, notificationContent, true); - } - LogUtil.info(BookingService.class,"End send notification function"); +// //Send Notification to Booker when booking lastOpera status Changed +// if (Strings.CS.equals(daylightBooking.getLastOperaStatus(),"RESERVED") && !Strings.CS.equals(dbBookingStatus,daylightBooking.getLastOperaStatus())){ +// LogUtil.info(BookingService.class,"Booking last opera status changed, DB Record status:"+dbBookingStatus+", DlBooking status:"+daylightBooking.getLastOperaStatus()); +// //设置Notification 内容 +// String notificationTitle = "#EN# Room status updated#EN#" + +// "#CN#房間狀態已更改#CN#"; +// String notificationContent = "#EN#Player {playerId}, {propertyName}, {roomCode},room status changed to reserved.#EN#" + +// "#CN#Player {playerId}, {propertyName}, {roomCode}, 房間狀態已預訂。#CN#"; +// String[] placeholders = { +// "{playerId}", "{propertyName}", "{roomCode}" +// }; +// LogUtil.info(BookingService.class,"Set notification info"); +// String[] values = { +// StringUtils.defaultString(dbBooking.getPlayerId()), +// StringUtils.defaultString(property == null ? null : property.getName()), +// StringUtils.defaultString(dbBooking.getRoomTypeCode()) +// }; +// LogUtil.info(BookingService.class,"Replace each"); +// notificationContent = StringUtils.replaceEach(notificationContent, placeholders, values); +// String transactionId = "DLBKSC"+dbBooking.getId(); +// LogUtil.info(BookingService.class,"Get device token from DB Start"); +// LogUtil.info(BookingService.class,"dbBooking.getCreatedBy()=" + dbBooking.getCreatedBy()); +// SysLogDeviceToken deviceToken = sysLogDeviceTokenRepository.findByUserId(StringUtils.trim(dbBooking.getCreatedBy())); +// LogUtil.info(BookingService.class,"Get device token from DB:" + (deviceToken == null ? "null" : deviceToken.getDeviceToken())); +// String deviceTokenValue =null; +// String finalTitle = null; +// String finalContent = null; +// if(deviceToken!=null){ +// deviceTokenValue = deviceToken.getDeviceToken(); +// finalTitle = notificationUtil.distinguishLanguage(notificationTitle,deviceToken.getUseLanguageType()); +// notificationContent = notificationUtil.distinguishLanguage(notificationContent,deviceToken.getUseLanguageType()); +// +// } +//// NOTIFICATION +// apnNotificationService.pushApnMessage(transactionId,deviceTokenValue,finalTitle,notificationContent,true); +// } +// +// LogUtil.info(BookingService.class,"Daylight webhook updated, prepare send notification"); +// log.info("dbBooking checkin={}, checkout={}", checkInDate, checkOutDate); +// log.info("dlBooking checkin={}, checkout={}", daylightBooking.getCheckinDate(), daylightBooking.getCheckoutDate()); +// LogUtil.info(BookingService.class,"Db booking status=" + dbBookingStatus); +// //Send notification to Booker when Booking C/I | C/O date changed +// if ((checkInDate != null && !checkInDate.equals(daylightBooking.getCheckinDate()))|| +// (checkOutDate != null && !checkOutDate.equals(daylightBooking.getCheckoutDate()))) { +// LogUtil.info(BookingService.class,"Booking date changed, DB Record C/I:"+checkInDate+", DB Record C/O:"+checkOutDate+ +// ", Dl Record C/I"+daylightBooking.getCheckinDate()+", Dl Record C/O"+daylightBooking.getCheckoutDate()); +// +// +// //设置Notification 内容 +// String notificationTitle = "#EN# Room status updated#EN#" + +// "#CN#房間狀態已更改#CN#"; +// String notificationContent = "#EN#Player {playerId}, {propertyName}, {roomCode},Stay date changed.#EN#" + +// "#CN#Player {playerId}, {propertyName}, {roomCode}, 住宿日期已變更。#CN#"; +// String[] placeholders = { +// "{playerId}", "{propertyName}", "{roomCode}" +// }; +// String[] values = { +// StringUtils.defaultString(dbBooking.getPlayerId()), +// StringUtils.defaultString(property == null ? null : property.getName()), +// StringUtils.defaultString(dbBooking.getRoomTypeCode()) +// }; +// +// notificationContent = StringUtils.replaceEach(notificationContent, placeholders, values); +// String transactionId = "DLBKDC" + dbBooking.getId(); +// +// SysLogDeviceToken deviceToken = sysLogDeviceTokenRepository.findByUserId(StringUtils.trim(dbBooking.getCreatedBy())); +// LogUtil.info(BookingService.class,"Get device token from DB:" + (deviceToken == null ? "null" : deviceToken.getDeviceToken())); +// String deviceTokenValue = null; +// String finalTitle = null; +// String finalContent = null; +// if (deviceToken != null) { +// LogUtil.info(BookingService.class,"Get device Token success"); +// deviceTokenValue = deviceToken.getDeviceToken(); +// finalTitle = notificationUtil.distinguishLanguage(notificationTitle, deviceToken.getUseLanguageType()); +// notificationContent = notificationUtil.distinguishLanguage(notificationContent, deviceToken.getUseLanguageType()); +// } +//// NOTIFICATION +// apnNotificationService.pushApnMessage(transactionId, deviceTokenValue, finalTitle, notificationContent, true); +// } +// LogUtil.info(BookingService.class,"End send notification function"); } public Bookings findOneByDlProfileId(String dlProfileId) { diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 68ede25..83467ed 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -64,6 +64,7 @@ mcrm: username: password: ssl-bundle-name: + skip-trust-validation: true server-url: https://eu1.api.uat.development.abovecloud.io request-timeout: 30 token: @@ -85,7 +86,8 @@ mcrm: username: password: ssl-bundle-name: - server-url: + skip-trust-validation: true + server-url: https://mgpmsdevows45.dev.mgmmacao.org request-timeout: 30 security: authorization-data: "[MCRM_APP/securItykeyOfCrmapp87895312],[MCRM_CMS/seCuritykeyOfCrmCms8wetw432]" @@ -97,16 +99,37 @@ mcrm: username: password: ssl-bundle-name: - server-url: https://hca.mcmsapoc.com + skip-trust-validation: true + server-url: https://mmesbdevapp14.dev.mgmmacao.org request-timeout: 30 + api-path: + mcrm: /api/crmapp/v1.0 + limo: /api/limo/v1.0 token: token-url: /RequestAccessToken - app-id: crmappid - app-secret: crmsecuritykey + mcrm: + app-id: mgmtestuser001 + app-secret: MGM@!666 + limo: + app-id: crmappid + app-secret: crmsecuritykey request-timeout: 30 default-expiry-second: 700 security: authorization-data: "[MCRM_APP/securItykeyOfCrmapp87895312],[MCRM_CMS/seCuritykeyOfCrmCms8wetw432]" + limo: + proxy-setting: + host: + port: + auth: + username: + password: + ssl-bundle-name: + skip-trust-validation: true + server-url: http://mmlimodevapp01 + request-timeout: 30 + security: + authorization-data: "[MCRM_APP/securItykeyOfCrmapp87895312],[MCRM_CMS/seCuritykeyOfCrmCms8wetw432]" notification: email-sender: smtp-server: smtp.qq.com @@ -125,7 +148,7 @@ server: uri-encoding: UTF-8 logging: - config: classpath:logback.xml + config: classpath:log4j2-spring.xml springdoc: swagger-ui: @@ -155,4 +178,5 @@ apns: # host: api.push.apple.com daylight: - url: http://localhost:9087/api/daylight/proxy \ No newline at end of file + url: http://localhost:9087/api/daylight/proxy + system-id: 3a151ad3-46be-4644-b656-71d3b037b309 diff --git a/src/main/resources/log4j2-spring.xml b/src/main/resources/log4j2-spring.xml new file mode 100644 index 0000000..b4bc893 --- /dev/null +++ b/src/main/resources/log4j2-spring.xml @@ -0,0 +1,41 @@ + + + + api_gateway_runtime/logs + %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml deleted file mode 100644 index 3545290..0000000 --- a/src/main/resources/logback.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%-5level] %logger{50} - %msg%n - - - - - - - ${LOG_APP_HOME}/base.%d{yyyy-MM-dd}.log - - - ${LOG_MAX_HISTORY} - - - - %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{500} - %msg%n - - - - - - - - - - - - - - - - - - - - -