From 5c3fd6eff4391b176989ddc2aa423e880cc24138 Mon Sep 17 00:00:00 2001 From: 13009 Date: Wed, 3 Jul 2024 15:52:25 +0800 Subject: [PATCH] feat: channel group test --- .../api/core/cipher/DefaultCredential.java | 4 +- .../scfs/api/core/http/AbstractApiClient.java | 1 + .../api/core/http/EncryptRequestBody.java | 17 +- .../scfs/api/core/http/FileRequestBody.java | 22 ++- .../com/czcb/scfs/api/core/ConstantsTest.java | 1 + .../api/core/http/EncryptRequestBodyTest.java | 11 ++ .../api/core/http/FileRequestBodyTest.java | 14 ++ .../client/ApacheHttpclientGroupTest.java | 162 ++++++++++++++++++ .../http/client/ApacheHttpclientTest.java | 6 + .../http/client/TestPrivacyEncryptor.java | 2 +- 10 files changed, 232 insertions(+), 8 deletions(-) create mode 100644 scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientGroupTest.java diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/DefaultCredential.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/DefaultCredential.java index 39860fa..0f2f253 100644 --- a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/DefaultCredential.java +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/DefaultCredential.java @@ -89,7 +89,7 @@ public class DefaultCredential implements Credential { private String getChannelNo(HttpRequest request, Channel channel) { if (channel.isChannelGroupMode()) { - return Strings.toStr(Objects.requireNonNull(request.getRequestBody().getExtraParams().get(CHANNEL_NO))); + return Strings.toStr(Objects.requireNonNull(request.getRequestBody().getExtraParams().get(CHANNEL_NO), "渠道编号为空")); } return channel.getChannelNo(); @@ -97,7 +97,7 @@ public class DefaultCredential implements Credential { private String getAppNo(HttpRequest request, Channel channel) { if (channel.isChannelGroupMode()) { - return Strings.toStr(Objects.requireNonNull(request.getRequestBody().getExtraParams().get(APP_NO))); + return Strings.toStr(Objects.requireNonNull(request.getRequestBody().getExtraParams().get(APP_NO), "应用编码为空")); } return channel.getAppNo(); diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/AbstractApiClient.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/AbstractApiClient.java index 70bcfd7..81d6a96 100644 --- a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/AbstractApiClient.java +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/AbstractApiClient.java @@ -139,6 +139,7 @@ public abstract class AbstractApiClient implements ApiClient { String ciphertext = cipher.encrypt(secretKey, originalBody); return httpRequest.newBuilder() .body(new EncryptRequestBody.Builder() + .extraParams(httpRequest.getRequestBody().getExtraParams()) .originalBody(originalBody) .body(ciphertext) .build() diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/EncryptRequestBody.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/EncryptRequestBody.java index 3a7fb81..b9875cc 100644 --- a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/EncryptRequestBody.java +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/EncryptRequestBody.java @@ -3,7 +3,9 @@ package com.czcb.scfs.api.core.http; import com.czcb.scfs.api.core.util.Strings; import java.util.Collections; +import java.util.HashMap; import java.util.Map; +import java.util.Objects; /** * @author wangwei @@ -14,10 +16,13 @@ public class EncryptRequestBody implements RequestBody { private final byte[] body; // 原始内容 private final byte[] originalBody; + // 额外参数 + private final Map extraParams; private EncryptRequestBody(Builder builder) { this.body = builder.body; this.originalBody = builder.originalBody; + this.extraParams = builder.extraParams; } @Override @@ -36,12 +41,13 @@ public class EncryptRequestBody implements RequestBody { @Override public Map getExtraParams() { - return Collections.emptyMap(); + return Collections.unmodifiableMap(extraParams); } public static class Builder { private byte[] body; private byte[] originalBody; + private Map extraParams; public Builder body(String body) { this.body = Strings.toBytes(body); @@ -58,7 +64,16 @@ public class EncryptRequestBody implements RequestBody { return this; } + public Builder extraParams(Map extraParams) { + this.extraParams = extraParams; + return this; + } + public EncryptRequestBody build() { + if (Objects.isNull(this.extraParams)) { + this.extraParams = new HashMap<>(); + } + return new EncryptRequestBody(this); } } diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/FileRequestBody.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/FileRequestBody.java index 5264f17..0d444b0 100644 --- a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/FileRequestBody.java +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/FileRequestBody.java @@ -3,7 +3,9 @@ package com.czcb.scfs.api.core.http; import com.czcb.scfs.api.core.util.Strings; import java.util.Collections; +import java.util.HashMap; import java.util.Map; +import java.util.Objects; import static java.util.Objects.requireNonNull; @@ -12,10 +14,12 @@ import static java.util.Objects.requireNonNull; * @since 2.0.0 */ public class FileRequestBody implements RequestBody { - private final byte[] body; + private final byte[] body; // 额外参数 + private final Map extraParams; - private FileRequestBody(byte[] body) { + private FileRequestBody(byte[] body, Map extraParams) { this.body = body; + this.extraParams = extraParams; } @Override @@ -30,11 +34,12 @@ public class FileRequestBody implements RequestBody { @Override public Map getExtraParams() { - return Collections.emptyMap(); + return Collections.unmodifiableMap(extraParams); } public static class Builder { private byte[] body; + private Map extraParams; public Builder body(String body) { this.body = Strings.toBytes(body); @@ -46,10 +51,19 @@ public class FileRequestBody implements RequestBody { return this; } + public Builder extraParams(Map extraParams) { + this.extraParams = extraParams; + return this; + } + public FileRequestBody build() { requireNonNull(body); - return new FileRequestBody(body); + if (Objects.isNull(extraParams)) { + this.extraParams = new HashMap<>(); + } + + return new FileRequestBody(body, extraParams); } } } diff --git a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/ConstantsTest.java b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/ConstantsTest.java index fa5e700..4f77453 100644 --- a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/ConstantsTest.java +++ b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/ConstantsTest.java @@ -15,6 +15,7 @@ class ConstantsTest { Assertions.assertEquals("X-SCFS-Secret-Key", Constants.SECRET_KEY); Assertions.assertEquals("X-SCFS-Nonce", Constants.NONCE); Assertions.assertEquals("X-SCFS-Timestamp", Constants.TIMESTAMP); + Assertions.assertEquals("X-SCFS-Channel-Group", Constants.CHANNEL_GROUP); Assertions.assertEquals("X-SCFS-Request-Id", Constants.REQUEST_ID); Assertions.assertEquals("X-SCFS-IP-Address", Constants.IP_ADDRESS); Assertions.assertEquals("X-SCFS-Mac-Address", Constants.MAC_ADDRESS); diff --git a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/EncryptRequestBodyTest.java b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/EncryptRequestBodyTest.java index e17f5d7..55bd120 100644 --- a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/EncryptRequestBodyTest.java +++ b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/EncryptRequestBodyTest.java @@ -4,6 +4,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; +import java.util.HashMap; class EncryptRequestBodyTest { @@ -11,6 +12,7 @@ class EncryptRequestBodyTest { void getBody() { EncryptRequestBody requestBody = new EncryptRequestBody.Builder() .body("123456") + .extraParams(new HashMap<>()) .build(); Assertions.assertArrayEquals("123456".getBytes(StandardCharsets.UTF_8), requestBody.getBody()); @@ -27,6 +29,15 @@ class EncryptRequestBodyTest { Assertions.assertArrayEquals("abc".getBytes(StandardCharsets.UTF_8), requestBody.getOriginalBody()); } + @Test + void getOriginalBodyByte() { + EncryptRequestBody requestBody = new EncryptRequestBody.Builder() + .originalBody("abc".getBytes(StandardCharsets.UTF_8)) + .build(); + + Assertions.assertArrayEquals("abc".getBytes(StandardCharsets.UTF_8), requestBody.getOriginalBody()); + } + @Test void getContentType() { EncryptRequestBody requestBody = new EncryptRequestBody.Builder() diff --git a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/FileRequestBodyTest.java b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/FileRequestBodyTest.java index 4177cae..40eaff6 100644 --- a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/FileRequestBodyTest.java +++ b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/FileRequestBodyTest.java @@ -6,6 +6,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; +import java.util.HashMap; class FileRequestBodyTest { @@ -26,6 +27,19 @@ class FileRequestBodyTest { Assertions.assertTrue(requestBody.getExtraParams().isEmpty()); } + @Test + void testExtraParams() { + FileRequestBody requestBody = new FileRequestBody.Builder() + .body("123".getBytes(StandardCharsets.UTF_8)) + .extraParams(new HashMap<>()) + .build(); + + Assertions.assertArrayEquals("123".getBytes(StandardCharsets.UTF_8), requestBody.getBody()); + + Assertions.assertNotNull(requestBody.getExtraParams()); + Assertions.assertTrue(requestBody.getExtraParams().isEmpty()); + } + @Test void getContentType() { FileRequestBodyTest.TestApiFileRequest fileRequest = new FileRequestBodyTest.TestApiFileRequest(); diff --git a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientGroupTest.java b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientGroupTest.java new file mode 100644 index 0000000..17b4527 --- /dev/null +++ b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientGroupTest.java @@ -0,0 +1,162 @@ +package com.czcb.scfs.api.core.http.client; + +import com.czcb.scfs.api.core.*; +import com.czcb.scfs.api.core.cipher.*; +import com.czcb.scfs.api.core.http.*; +import com.czcb.scfs.api.core.util.DateTimes; +import com.czcb.scfs.api.core.util.Nonce; +import com.czcb.scfs.api.core.util.Strings; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockserver.client.MockServerClient; +import org.mockserver.junit.jupiter.MockServerExtension; +import org.mockserver.junit.jupiter.MockServerSettings; + +import java.nio.charset.StandardCharsets; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.List; + +import static com.czcb.scfs.api.core.Constants.*; +import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; + +@ExtendWith(MockServerExtension.class) +@MockServerSettings(ports = {8888}) +class ApacheHttpclientGroupTest { + private MockServerClient client; + + @BeforeEach + public void beforeEachLifecyleMethod(MockServerClient client) { + this.client = client; + } + + /** + * 构建配置 + */ + private Profile profile() { + PrivateKey privateKey = KeyText.loadTestPrivateKeyRSA(); + X509Certificate certificate = KeyText.loadTestRSA(); + List list = new ArrayList<>(); + list.add(certificate); + CertificateProvider certificateProvider = new LocalCertificateProvider(list); + Privacy privacy = new TestPrivacy(privateKey, certificateProvider); + Signature signature = new DefaultSignature(certificateProvider, + new TestSigner(privateKey, "6CDDAA92CAD75998325027647847330C1756291"), + new TestVerifier(certificateProvider)); + + return new TestProfile( + privacy, signature, new DefaultChannel.Builder() + .channelNo("0000") + .appNo("100000") + .enableChannelGroupMode() + .build(), new DefaultHttpProfile.Builder() + .online(false) + .logLevel(LogLevel.FULL) + .compressionEnabled(false) + .host("http://127.0.0.1:8888") + .build() + ); + } + + @Test + void doRemoteExecute() { + TestResponse mockResponse = new TestResponse(); + mockResponse.setName("123456"); + // mock 结果 + ApiClient apiClient = mock(mockResponse.toJsonResponse().getBody()); + + // 请求参数 + TestRequest request = new TestRequest(); + request.setChannelNo("300000"); + request.setAppNo("9999"); + + // 发起调用 + HttpResponse response = apiClient.post("/mock/xxx", request.toJsonRequest(), TestResponse.class); + + assertThatJson("{\"name\":\"123456\"}").isEqualTo(response.getServiceResponse().toJsonResponse().getBody()); + } + + private ApiClient mock(String body) { + ApiClient apiClient = ApiClientBuilder.custom() + .profile(profile()) + .build(); + + // 对称密钥 + byte[] secret = apiClient.getProfile().getPrivacy().getSecretCipher().getSecretKey(); + // 加密响应报文 + String responseBody = apiClient.getProfile().getPrivacy().getSecretCipher().encrypt(secret, body.getBytes(StandardCharsets.UTF_8)); + + // 加密对称密钥 + String secretKey = apiClient.getProfile().getPrivacy().getEncryptor().encrypt(Strings.toStr(secret)); + + org.mockserver.model.HttpResponse mock = response() + .withHeader(NONCE, Nonce.ofNonce()) + .withHeader(SECRET_KEY, secretKey) + .withHeader(REQUEST_ID, Nonce.ofNonce()) + .withHeader(BANK_CERTIFICATE_SERIAL, "6CDDAA92CAD75998325027647847330C1756291") + .withHeader(CHANNEL_CERTIFICATE_SERIAL, "6CDDAA92CAD75998325027647847330C1756291") + .withHeader(TIMESTAMP, DateTimes.ofTimestamp()); + + mock.withBody(responseBody); + + String buildAuth = NONCE + "=" + mock.getHeader(NONCE).get(0) + "," + + TIMESTAMP + "=" + mock.getHeader(TIMESTAMP).get(0) + "," + + BANK_CERTIFICATE_SERIAL + "=" + mock.getHeader(BANK_CERTIFICATE_SERIAL).get(0) + "," + + CHANNEL_CERTIFICATE_SERIAL + "=" + mock.getHeader(CHANNEL_CERTIFICATE_SERIAL).get(0) + "," + + SECRET_KEY + "=" + secretKey; + + String message = buildAuth + "\n" + responseBody + "\n"; + mock.withHeader(SIGNATURE, apiClient.getProfile().getSignature().getSigner().sign(message).getSignature()); + + client.when(request() + .withHeader("Authorization", "SCFS-SHA256withRSA X-SCFS-Channel-No=300000,X-SCFS-App-No=9999,X-SCFS-Signature=.*") + .withHeader("X-SCFS-Channel-Group", "0000") + .withHeader("X-SCFS-Nonce", ".*") + .withHeader("X-SCFS-Secret-Key", ".*") + .withHeader("X-SCFS-Serial", "6CDDAA92CAD75998325027647847330C1756291") + .withHeader("X-SCFS-Channel-Serial", "6CDDAA92CAD75998325027647847330C1756291") + .withHeader("X-SCFS-Timestamp", ".*") + .withMethod(HttpMethod.POST.getUpperName()) + .withPath("/mock/xxx") + ).respond(mock); + + return apiClient; + } + + public static class TestRequest implements ApiRequest { + private String channelNo; + private String appNo; + + public String getChannelNo() { + return channelNo; + } + + public void setChannelNo(String channelNo) { + this.channelNo = channelNo; + } + + public String getAppNo() { + return appNo; + } + + public void setAppNo(String appNo) { + this.appNo = appNo; + } + } + + public static class TestResponse implements ApiResponse { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } +} \ No newline at end of file diff --git a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientTest.java b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientTest.java index 58a7b3a..3fc34d7 100644 --- a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientTest.java +++ b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientTest.java @@ -138,6 +138,12 @@ class ApacheHttpclientTest { mock.withHeader(SIGNATURE, apiClient.getProfile().getSignature().getSigner().sign(message).getSignature()); client.when(request() + .withHeader("Authorization", "SCFS-SHA256withRSA X-SCFS-Channel-No=0000,X-SCFS-App-No=100000,X-SCFS-Signature=.*") + .withHeader("X-SCFS-Nonce", ".*") + .withHeader("X-SCFS-Secret-Key", ".*") + .withHeader("X-SCFS-Serial", "6CDDAA92CAD75998325027647847330C1756291") + .withHeader("X-SCFS-Channel-Serial", "6CDDAA92CAD75998325027647847330C1756291") + .withHeader("X-SCFS-Timestamp", ".*") .withMethod(HttpMethod.POST.getUpperName()) .withPath("/mock/xxx") ).respond(mock); diff --git a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/TestPrivacyEncryptor.java b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/TestPrivacyEncryptor.java index c777722..fb996dd 100644 --- a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/TestPrivacyEncryptor.java +++ b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/TestPrivacyEncryptor.java @@ -11,6 +11,6 @@ public class TestPrivacyEncryptor extends AbstractPrivacyEncryptor { * @param publicKey 加密使用的公钥 */ public TestPrivacyEncryptor(PublicKey publicKey) { - super("RSA/ECB/OAEPWithSHA-1AndMGF1Padding", publicKey, null, ""); + super("RSA/ECB/OAEPWithSHA-1AndMGF1Padding", publicKey, null, "6CDDAA92CAD75998325027647847330C1756291"); } }