Compare commits

...

2 Commits

Author SHA1 Message Date
tianhao 849aa32f4f feat:通知请求参数解密返回参数T支持String类型 2026-05-26 11:22:16 +08:00
tianhao 4615b3fccd feat:封装客户端通知处理 2026-05-20 17:21:43 +08:00
4 changed files with 169 additions and 0 deletions

View File

@ -1,11 +1,19 @@
package com.czcb.scfs.api.core;
import com.czcb.scfs.api.core.cipher.PrivacyEncryptor;
import com.czcb.scfs.api.core.cipher.SecretCipher;
import com.czcb.scfs.api.core.http.*;
import com.czcb.scfs.api.core.util.Extractor;
import com.czcb.scfs.api.core.util.Json;
import com.czcb.scfs.api.core.util.Strings;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.util.Map;
import static com.czcb.scfs.api.core.Constants.BANK_CERTIFICATE_SERIAL;
import static com.czcb.scfs.api.core.Constants.SECRET_KEY;
/**
* @author wangwei
* @since 2.0.0
@ -70,4 +78,66 @@ public interface ApiClient {
.build();
return exchange(httpRequest, responseClass);
}
/**
*
*
* @param requestJsonStr JSON
* @param requestClass Class
* @return
* @author H.T
* @since 2026/5/19
*/
default <T> T decryptRequest(String requestJsonStr, Class<T> requestClass) {
// 解析JSON
JsonObject requestJson = JsonParser.parseString(requestJsonStr).getAsJsonObject();
// 提取请求头
JsonObject headerJson = requestJson.getAsJsonObject("header");
// SECRET_KEY密钥 密文
String secretKeyCiphertext = headerJson.get(SECRET_KEY).getAsString();
// 非对称解密后SECRET_KEY密钥 明文
String secretKeyPlain = getProfile().getPrivacy().getDecryptor().decrypt(secretKeyCiphertext);
// 提取请求体原始body 密文
String bodyCiphertext = requestJson.get("body").getAsString();
// 对称解密后body 明文
String bodyPlain = getProfile().getPrivacy().getSecretCipher().decrypt(Strings.toBytes(secretKeyPlain), Strings.toBytes(bodyCiphertext));
// T支持返回String
return requestClass == String.class ? requestClass.cast(bodyPlain) : Json.fromJson(bodyPlain, requestClass);
}
/**
*
*
* @param responseObject Object
* @return JSON
* @author H.T
* @since 2026/5/19
*/
default String encryptResponse(Object responseObject) {
// 序列化body 明文
String responseJsonStr = Json.toJson(responseObject);
// 对称加密器
SecretCipher cipher = getProfile().getPrivacy().getSecretCipher();
// 生成SECRET_KEY密钥 明文
byte[] secretKeyPlain = cipher.getSecretKey();
// 非对称加密器
PrivacyEncryptor encryptor = getProfile().getPrivacy().getEncryptor();
// 非对称加密后SECRET_KEY密钥 密文
String secretKeyCiphertext = encryptor.encrypt(Strings.toStr(secretKeyPlain));
// 对称加密后body 密文
String bodyCiphertext = cipher.encrypt(secretKeyPlain, Strings.toBytes(responseJsonStr));
// 应答JSON串
JsonObject responseJson = new JsonObject();
// 应答头JSON串
JsonObject headerJson = new JsonObject();
// 加密后的密钥放入到请求头
headerJson.addProperty(SECRET_KEY, secretKeyCiphertext);
// 加密证书序列号放入到请求头
headerJson.addProperty(BANK_CERTIFICATE_SERIAL, encryptor.getCertificateSerial());
// 添加应答头
responseJson.add("header", headerJson);
// 添加body
responseJson.addProperty("body", bodyCiphertext);
return responseJson.toString();
}
}

View File

@ -0,0 +1,43 @@
package com.czcb.scfs.api.service.handle;
import com.czcb.scfs.api.core.ApiClient;
/**
*
*
* @author H.T
* @since 2026-05-19
*/
public class NotifyHandler {
private final ApiClient apiClient;
public NotifyHandler(ApiClient apiClient) {
this.apiClient = apiClient;
}
/**
*
*
* @param requestJsonStr JSONheaderbody
* @param requestClass Class
* @return Object
* @author H.T
* @since 2026/5/19
*/
public <T> T decryptRequest(String requestJsonStr, Class<T> requestClass) {
return apiClient.decryptRequest(requestJsonStr, requestClass);
}
/**
*
*
* @param responseObject Object
* @return JSONheaderbody
* @author H.T
* @since 2026/5/19
*/
public String encryptResponse(Object responseObject) {
return apiClient.encryptResponse(responseObject);
}
}

View File

@ -4,6 +4,7 @@ import com.czcb.scfs.api.core.ApiClient;
import com.czcb.scfs.api.core.Profile;
import com.czcb.scfs.api.core.http.ApiClientBuilder;
import com.czcb.scfs.api.service.echo.EchoService;
import com.czcb.scfs.api.service.handle.NotifyHandler;
import com.czcb.scfs.api.service.v2.account.AccountService;
import com.czcb.scfs.api.service.v2.ar.*;
import com.czcb.scfs.api.service.v2.bills.BillService;
@ -348,4 +349,14 @@ public class ScfsAutoConfiguration {
public SummaryOrderService summaryOrderService(ApiClient apiClient) {
return new SummaryOrderService(apiClient);
}
/**
*
*/
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(NotifyHandler.class)
public NotifyHandler notifyHandler(ApiClient apiClient) {
return new NotifyHandler(apiClient);
}
}

View File

@ -0,0 +1,45 @@
package com.czcb.scfs.api.test.service;
import com.czcb.scfs.api.service.handle.NotifyHandler;
import com.czcb.scfs.api.service.v2.pay.model.OfflineRechargeRefundRequest;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import static com.czcb.scfs.api.core.Constants.SECRET_KEY;
/**
* Test
*
* @author H.T
* @since 2026-05-19
*/
@SpringBootTest
class NotifyHandlerTest {
@Resource
private NotifyHandler notifyHandler;
@Test
void decryptRequest() throws JSONException {
JSONObject jsonObject = new JSONObject();
jsonObject.put("body", "n/mpGUWGb9HpNvtyM1U7s8yTHP1oMBqAR4U+hTvw0gMtHFcXdvltJ/Dcw+U079FFiClExTP+d6skwnVVCftRXOfWlwOtQ9Ws1Soq7m+Gpr5CPuaMsRZeqzM4QL/RWhvGX36yE8BWZfICIBgmF8GdBd12iOjd6UO5qrt6ZZjqC0wr0gWpU9HK4JdcbJhaWOg45q1a3A76DVVTOsavwIfjF742WrVg3t42/2NsP7GNkMBJyuv1XHJQGgAUGP0Q6l5vPNU8nAFxyC+gKX/CyY+hmsqAr+jNOTVrcVj/AApQ3zf/pI1/zInVR6cq76ulv/5U2rBFrTJpZk6hpiFgr8/OfRYzrn0dQ/0QaSLFG8L0prIkMvV/");
JSONObject headerJson = new JSONObject();
headerJson.put(SECRET_KEY, "MHoCIQC1ZJNfSItiTtyGrPY4xxHvTKiFMxSEJtlWlG+iKGGMnQIhAM4hAOEdCoKDZS/GH/l/kxtgmtC4we2Cxtt4SXQroxkOBCA2GwL0sfxtqc8I6MU6hbmgby1xEHAq61l+3OiOZOkKOAQQBPDf5gnp3cB701SGw9i7cg==");
jsonObject.put("header", headerJson);
System.err.println(jsonObject);
System.out.println(notifyHandler.decryptRequest(jsonObject.toString(), OfflineRechargeRefundRequest.class));
System.out.println(notifyHandler.decryptRequest(jsonObject.toString(), String.class));
}
@Test
void encryptResponse() {
System.out.println(notifyHandler.encryptResponse("通知成功"));
}
}