package com.nitccs.jit.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.mysql.cj.core.util.StringUtils;
import com.nitccs.jit.entity.JITAuthRet;
import com.nitccs.jit.entity.JITRet;
import com.nitccs.jit.excption.CryptoException;
import com.nitccs.jit.utils.Base64Util;
import com.nitccs.jit.utils.ObjectUtil;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author 华
* @date 2025/6/18
* @desc JIT 密管平台基础服务
*/
@Service
public class JITBaseService {
private static final Logger logger = org.slf4j.LoggerFactory.getLogger(JITBaseService.class);
@Value("${jit.url}")
String url;
@Value("${jit.appId}")
String appId;
@Value("${jit.appPwd}")
String appPwd;
@Value("${jit.storageKeyCode}")
String storageKeyCode; // 存储加解密
@Value("${jit.cookieName:KMS_SESSIONID}")
String cookieName;
final String version = "@V1";
private final Map<String, String> DEFAULT_HEADERS = new ConcurrentHashMap<>();
private final List<NameValuePair> DEFAULT_PARAMS = new ArrayList<>();
HttpClient httpClient;
public JITBaseService() {
}
// 初始化默认请求头和参数
@PostConstruct
private void init() {
if (StringUtils.isNullOrEmpty(url)) {
throw new CryptoException("请配置平台地址!");
}
if (StringUtils.isNullOrEmpty(appId) || StringUtils.isNullOrEmpty(appPwd)) {
throw new CryptoException("请配置平台标识和密钥!");
}
DEFAULT_HEADERS.put("Content-Type", "application/json");
DEFAULT_HEADERS.put("Authorization", "Basic " + Base64Util.encode(appId + ":" + appPwd));
DEFAULT_PARAMS.add(new NameValuePair("keyCode", storageKeyCode));
DEFAULT_PARAMS.add(new NameValuePair("algorithmParam", "SM4/CBC/PKCS7Padding"));
try {
MultiThreadedHttpConnectionManager manager = new MultiThreadedHttpConnectionManager();
httpClient = new HttpClient(manager);
httpClient.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
URL platformUrl = new URL(url);
// 配置主机信任证书的SSL上下文
registerHttpsProtocol(platformUrl.getHost(), platformUrl.getPort());
// 配置baseURL
httpClient.getHostConfiguration().setHost(platformUrl.getHost(), platformUrl.getPort(), platformUrl.getProtocol());
// 连接超时时间 5s,读取超时时间 30s
httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(5 * 1000);
httpClient.getParams().setSoTimeout(30 * 1000);
logger.info("httpClient 初始化成功,平台地址:{}", url);
} catch (MalformedURLException e) {
throw new CryptoException(String.format("【%s】平台地址无效!", url));
} catch (Exception e) {
throw new CryptoException("初始化HTTP客户端失败", e);
}
}
/**
* 特定主机证书验证
*/
private void registerHttpsProtocol(String mHost, int mPort) {
try {
// 创建默认的SSL上下文(用于正常验证)
SSLContext defaultSSLContext = SSLContext.getInstance("TLS");
defaultSSLContext.init(null, null, null);
// 创建信任所有证书的SSL上下文(用于特定主机)
SSLContext trustAllSSLContext = SSLContext.getInstance("TLS");
trustAllSSLContext.init(null, new TrustManager[]{
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() { return null; }
public void checkClientTrusted(X509Certificate[] certs, String authType) {}
public void checkServerTrusted(X509Certificate[] certs, String authType) {}
}
}, null);
SecureProtocolSocketFactory socketFactory = new SecureProtocolSocketFactory() {
@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
try {
// 对特定主机使用信任所有证书的上下文
if (Objects.equals(host, mHost) && Objects.equals(port, mPort)) {
SSLSocket sslSocket = (SSLSocket) trustAllSSLContext.getSocketFactory().createSocket(socket, host, port, autoClose);
return sslSocket;
}
// 其他主机使用默认上下文
return defaultSSLContext.getSocketFactory().createSocket(socket, host, port, autoClose);
} catch (Exception e) {
throw new IOException("创建SSL连接失败", e);
}
}
@Override
public Socket createSocket(String host, int port, java.net.InetAddress clientHost, int clientPort) throws IOException {
if (Objects.equals(host, mHost) && Objects.equals(port, mPort)) {
return trustAllSSLContext.getSocketFactory().createSocket(host, port, clientHost, clientPort);
}
return defaultSSLContext.getSocketFactory().createSocket(host, port, clientHost, clientPort);
}
@Override
public Socket createSocket(String host, int port) throws IOException {
if (Objects.equals(host, mHost) && Objects.equals(port, mPort)) {
return trustAllSSLContext.getSocketFactory().createSocket(host, port);
}
return defaultSSLContext.getSocketFactory().createSocket(host, port);
}
@Override
public Socket createSocket(String host, int port, java.net.InetAddress localAddress, int localPort, org.apache.commons.httpclient.params.HttpConnectionParams params) throws IOException {
if (Objects.equals(host, mHost) && Objects.equals(port, mPort)) {
return trustAllSSLContext.getSocketFactory().createSocket(host, port, localAddress, localPort);
}
return defaultSSLContext.getSocketFactory().createSocket(host, port, localAddress, localPort);
}
};
// 注册自定义的HTTPS协议
Protocol https = new Protocol("https", socketFactory, 443);
Protocol.registerProtocol("https", https);
logger.info("已注册自定义HTTPS协议,只对 {}:{} 忽略证书验证", mHost, mPort);
} catch (NoSuchAlgorithmException | KeyManagementException e) {
logger.error("配置SSL信任管理器失败", e);
throw new CryptoException("配置SSL信任管理器失败", e);
}
}
/**
* 进行身份认证,获取 cookie
*/
public void authentication() {
final String url = "/app/auth/v1/authentication";
Map<String, Object> params = new HashMap<>();
params.put("code", appId);
params.put("secret", appPwd);
JITAuthRet ret = post(params, url, JITAuthRet.class);
logger.info("JIT 口令认证结果:{}", ret);
// 认证成功后,设置cookie
Cookie cookie = new Cookie(httpClient.getHostConfiguration().getHost(), cookieName, ret.getToken(), "/api/", ret.getExpirationTime(), false);
httpClient.getState().addCookie(cookie);
logger.info("JIT 口令认证成功,设置cookie: {}", cookie);
}
/**
* 检查cookie是否过期,过期则重新认证
*/
public void validCookie() {
boolean needAuth = Arrays.stream(httpClient.getState().getCookies())
.noneMatch(cookie ->
cookieName.equals(cookie.getName()) && !cookie.isExpired()
);
if (needAuth) authentication();
}
/**
* 发送HTTPS POST请求 需要认证
* @param params 请求参数
* @param url 请求地址
* @param clazz 响应数据类型
* @return 响应数据
*/
public <T> T sendRequest(Map<String, Object> params, String url, Class<T> clazz) {
// 检查cookie是否过期,过期则重新认证
validCookie();
// 合并统一参数和请求参数
Map<String, Object> allParams = new HashMap<>();
for (NameValuePair pair : DEFAULT_PARAMS) {
allParams.put(pair.getName(), pair.getValue());
}
allParams.putAll(params);
return post(allParams, url, clazz);
}
/**
* 发送HTTPS POST请求
* @param params 请求参数
* @param url 请求地址
* @param clazz 响应数据类型
* @return 响应数据
*/
private <T> T post(Map<String, Object> params, String url, Class<T> clazz) {
PostMethod postMethod = null;
try {
postMethod = new PostMethod(this.url + url);
// 设置统一请求头
for (Map.Entry<String, String> header : DEFAULT_HEADERS.entrySet()) {
postMethod.setRequestHeader(header.getKey(), header.getValue());
}
// 合并统一参数和请求参数
String jsonBody = ObjectUtil.toJson(params);
// 设置请求体
postMethod.setRequestEntity(new StringRequestEntity(jsonBody, "application/json", "UTF-8"));
logger.info("请求URL: {}", url);
logger.info("请求头: {}", Arrays.toString(postMethod.getRequestHeaders()));
logger.info("请求体: {}", postMethod.getResponseBodyAsString());
logger.info("创建请求-URL={},headers={},params={}", url, postMethod.getRequestHeaders(), jsonBody);
// 执行请求
int statusCode = httpClient.executeMethod(postMethod);
if (statusCode == HttpStatus.SC_OK) {
String responseBody = postMethod.getResponseBodyAsString();
logger.info("请求成功,结果: {}", responseBody);
JITRet ret = ObjectUtil.toObject(responseBody, JITRet.class);
if (ret.isSuccess()) {
return ObjectUtil.convert(ret.getData(), clazz);
}
throw new CryptoException(ret.getMessage());
} else if (statusCode == HttpStatus.SC_INTERNAL_SERVER_ERROR) {
// 处理服务器内部错误
logger.warn("密管平台服务异常,请求失败,地址:{},状态码: {}", postMethod.getPath(), statusCode);
throw new CryptoException(String.format("服务器异常【%s】,请稍后再试!", statusCode));
}
else {
logger.warn("请求失败,地址:{},状态码: {}", postMethod.getPath(), statusCode);
throw new CryptoException(String.format("请求失败,地址:%s,状态码: %s", this.url + postMethod.getPath(), statusCode));
}
} catch (HttpException e) {
logger.error("HTTP协议错误", e);
throw new CryptoException(String.format("HTTP协议错误: %s", e.getMessage()));
} catch (IOException e) {
logger.error("读取错误响应体失败", e);
throw new CryptoException(String.format("读取错误响应体失败: %s", e.getMessage()));
} finally {
if (postMethod != null) {
postMethod.releaseConnection();
}
}
}
/**
* 合并请求参数
* @param params 请求参数
* @return JSON 字符串
* @throws JsonProcessingException
*/
private String combineParams(Map<String, Object> params) throws JsonProcessingException {
Map<String, Object> allParams = new HashMap<>();
for (NameValuePair pair : DEFAULT_PARAMS) {
allParams.put(pair.getName(), pair.getValue());
}
allParams.putAll(params);
return ObjectUtil.toJson(allParams);
}
}
发送请求返回500,但使用postman和浏览器发送请求正常