确保在部署环境中能正常忽略SSL,命令如下:
curl -vk -X POST --header ‘Content-Type: application/json’ --header ‘Accept: application/json’ --header ‘X-Forwarded-Host: peo-es2-in.ids.aaa.com’ -d ‘{“header”: {“functionRequesterIdentifier”: “PEOgId”,“functionCallIdentifier”: “websheet”}, “eid”: “111”,“icid”:“222”,“matchingId”: “”,“releaseFlag”: “true” }’ ‘https://abc-es2plus-in.prod.bcd.com:38443/gsma/rsp2/es2plus/confirmOrder’ --key /root/ssl/ES2-in/smdpp-peo-prod-es2plus.key --cert /root/ssl/ES2-in/smdpp-peo-prod-es2plus.pem
其中 curl -k 为忽略SSL参数
一、先把pem+key转p12文件。
openssl pkcs12 -export -out smdpp-peo-prod-es2plus.p12 -in /root/ssl/ES2-in/smdpp-peo-prod-es2plus.pem -inkey /root/ssl/ES2-in/smdpp-peo-prod-es2plus.key -name "SMDPP-UI-CLIENT2"
输入密码
Abcd@2024!
二、springboot 相关代码:
pom.xml
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.sm</groupId>
<artifactId>SM</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SM</name>
<description>Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 热部署 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>true</scope>
<optional>true</optional> <!-- 这个需要为 true 热部署才有效 -->
</dependency>
<!-- 热部署end -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.3</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.1</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20240303</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>spring-milestones</id>
<url>http://repo.spring.io/libs-milestone/</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork> <!-- 如果没有该配置,devtools不会生效 -->
</configuration>
</plugin>
</plugins>
</build>
</project>
application.yml
server:
port: 38088
servlet:
context-path: /
spring:
# 指定静态资源的路径
resources:
static-locations: classpath:/static/,classpath:/views/
thymeleaf:
prefix: classpath:/templates/
ssl: #ssl配置
p12: /root/ssl/ES2-in/smdpp-peo-prod-es2plus.p12
url: https://abc-es2plus-in.prod.bcd.com:38443/gsma/rsp2/es2plus/
forwardedHost: peo-es2-in.ids.aaa.com
errorMsg: '{"header":{"functionExecutionStatus":{"status":"Failed"}}}'
logging:
config: classpath:logback.xml
SSLVo.java ,通过此实例获取yml中的配置信息
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class SSLVo {
@Value("${ssl.p12}")
private String p12;
@Value("${ssl.url}")
private String url;
private String pwd;
@Value("${ssl.forwardedHost}")
private String forwardedHost;
@Value("${ssl.errorMsg}")
private String errorMsg;
public String getP12() {
return p12;
}
public String getUrl() {
return url;
}
public String getPwd() { // p12的密码
return "Abcd@2024!";
}
public String getForwardedHost() {
return forwardedHost;
}
public String getErrorMsg() {
return errorMsg;
}
}
IgnoreSSL.java 工具类,信任所有证书忽略SSL
import com.sm.vo.SSLVo;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import javax.net.ssl.*;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
public class IgnoreSSL {
public static SSLContext createSslContextWithTrustAllCerts(SSLVo sSLVo) throws Exception {
// PKCS#12文件路径和密码
String p12FilePath = sSLVo.getP12();
char[] p12Pwd = sSLVo.getPwd().toCharArray();
// 加载PKCS#12 KeyStore
KeyStore keyStore = KeyStore.getInstance("PKCS12");
try (InputStream inputStream = new FileInputStream(p12FilePath)) {
keyStore.load(inputStream, p12Pwd);
}
// 信任所有证书的TrustManager
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
// 不检查客户端证书
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
// 不检查服务器证书
}
}
};
// 初始化KeyManagerFactory
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, p12Pwd);
// 初始化TrustManagerFactory(如果你信任服务器的证书,你可以使用默认的TrustManager)
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
// 初始化SSLContext
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagerFactory.getKeyManagers(), trustAllCerts, new java.security.SecureRandom());
MultiValueMap<String, Object> headers = new LinkedMultiValueMap<String, Object>();
headers.add("Accept", "application/json");
headers.add("Content-Type", "application/json");
headers.add("X-Forwarded-Host", sSLVo.getForwardedHost());
return sslContext;
}
}
SMDSController.java
import com.sm.util.IgnoreSSL;
import com.sm.vo.SSLVo;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
@RestController
public class SMDSController {
@Autowired
private SSLVo sSLVo;
private static final Logger logger = LoggerFactory.getLogger(SMDSController.class);
@PostMapping("/confirmOrder")
public ResponseEntity<String> confirmOrder(@RequestBody HashMap<String, Object> map) throws Exception{
JSONObject requestBody = new JSONObject(map);
StringBuffer result = new StringBuffer();
String urlStr = sSLVo.getUrl() + "confirmOrder";
// 设置请求URL
URL url = new URL(urlStr);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
try {
SSLContext sslContext = IgnoreSSL.createSslContextWithTrustAllCerts(sSLVo);
// 设置连接属性
conn.setSSLSocketFactory(sslContext.getSocketFactory());
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json");
conn.setDoOutput(true);
// 发送请求体
try (OutputStream os = conn.getOutputStream()) {
byte[] input = requestBody.toString().getBytes("utf-8");
os.write(input, 0, input.length);
}
// 获取响应
int responseCode = conn.getResponseCode();
// 处理响应内容(如果有的话)
if (responseCode == HttpURLConnection.HTTP_OK) {
// 读取响应内容
try (java.io.BufferedReader in = new java.io.BufferedReader(
new java.io.InputStreamReader(conn.getInputStream()))) {
String inputLine;
while ((inputLine = in.readLine()) != null) {
result.append(inputLine);
}
}
} else {
result.append("confirmOrder__请求失败,responseCode:"+responseCode);
}
} catch (Exception e) {
logger.error("confirmOrder__Exception--------:", e);
} finally {
// 关闭连接
conn.disconnect();
logger.info("confirmOrder__requestBody--------:{}", requestBody.toString());
logger.info("confirmOrder__response--------:{}", result.toString());
if(StringUtils.isEmpty(result.toString())) {
result = new StringBuffer(sSLVo.getErrorMsg());
return new ResponseEntity<>(result.toString(), HttpStatus.REQUEST_TIMEOUT);
}
return new ResponseEntity<>(result.toString(), HttpStatus.OK);
}
}
}
logback.xml log的配置,按日期产生日志并保留30个文件。此处把Error级别的错误日志单独抽了一份出来
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
<property name="LOG_HOME" value="logs/" ></property>
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<!--<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>-->
<property name="PATTERN" value="%-12(%d{yyyy-MM-dd HH:mm:ss.SSS}) |-%-5level [%thread] %c [%L] -| %msg%n" />
<!--控制台日志, 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${PATTERN}</pattern>
</encoder>
</appender>
<!--文件日志, 按照每天生成日志文件 -->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/info.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_HOME}/info.%d{yyyy-MM-dd}.log</FileNamePattern>
<!--日志文件保留数-->
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${PATTERN}</pattern>
</encoder>
</appender>
<!-- 按照每天生成错误日志文件 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<file>${LOG_HOME}/error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_HOME}/error.%d{yyyy-MM-dd}.log</FileNamePattern>
<!--日志文件保留数-->
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${PATTERN}</pattern>
</encoder>
</appender>
<!-- 日志输出级别 -->
<root level="INFO">
<appender-ref ref="STDOUT" ></appender-ref>
<appender-ref ref="INFO_FILE" ></appender-ref>
<appender-ref ref="ERROR_FILE"/>
</root>
</configuration>
最后调用接口:
curl -v -X POST --header ‘Content-Type: application/json’ --header ‘Accept: application/json’ -d ‘{“header”: {“functionRequesterIdentifier”: “PEOgId”,“functionCallIdentifier”: “websheet”}, “eid”: “111”,“icid”:“222”,“matchingId”: “”,“releaseFlag”: “true” }’ ‘http://localhost:38088/confirmOrder’