test: 单元测试
parent
03e6ac3f9f
commit
33baa0436d
|
|
@ -49,22 +49,22 @@ public final class DefaultValidator implements Validator {
|
|||
public void isInvalidHttpCode(OriginalResponse response) {
|
||||
if (response.getStatusCode() < HTTP_OK || response.getStatusCode() >= HTTP_MULT_CHOICE) {
|
||||
throw new ValidationException(String.format("校验失败, HttpStatusCode=%s, Request-Id=%s, HttpResponseBody=%s",
|
||||
response.getStatusCode(), response.getHttpHeaders().getHeader(REQUEST_ID), plainBody(response)));
|
||||
response.getStatusCode(), response.getHttpHeaders().getHeader(REQUEST_ID), decryptResponseBodyIfNecessary(response)));
|
||||
}
|
||||
}
|
||||
|
||||
public void validateTimestamp(OriginalResponse response) {
|
||||
String timestamp = response.getHttpHeaders().getHeader(TIMESTAMP);
|
||||
if (Strings.isEmpty(timestamp)) {
|
||||
throw new TimestampException(String.format("校验失败, 时间戳[%s]不存在, Request-Id=%s, HttpResponseBody=%s", TIMESTAMP,
|
||||
response.getHttpHeaders().getHeader(REQUEST_ID), plainBody(response)));
|
||||
throw new TimestampException(String.format("校验失败, 时间戳[%s]不存在, Request-Id=%s, HttpResponseBody=%s",
|
||||
TIMESTAMP, response.getHttpHeaders().getHeader(REQUEST_ID), decryptResponseBodyIfNecessary(response)));
|
||||
}
|
||||
|
||||
Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestamp));
|
||||
// 拒绝过期请求
|
||||
if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= RESPONSE_EXPIRED_MINUTES) {
|
||||
throw new TimestampException(String.format("校验失败, 时间戳[%s=%s]已过期, Request-Id=%s, HttpResponseBody=%s", TIMESTAMP,
|
||||
timestamp, response.getHttpHeaders().getHeader(REQUEST_ID), plainBody(response)));
|
||||
throw new TimestampException(String.format("校验失败, 时间戳[%s=%s]已过期, Request-Id=%s, HttpResponseBody=%s",
|
||||
TIMESTAMP, timestamp, response.getHttpHeaders().getHeader(REQUEST_ID), decryptResponseBodyIfNecessary(response)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -72,31 +72,34 @@ public final class DefaultValidator implements Validator {
|
|||
// 待签名串
|
||||
String message = profile.getSignature().getCredential().buildResponseMessage(response, channel);
|
||||
String signature = response.getHttpHeaders().getHeader(SIGNATURE);
|
||||
if (signature == null || signature.isEmpty()) {
|
||||
throw new ValidationException(String.format("校验失败, 签名[%s]不存在, Request-Id=%s, HttpResponseBody=%s", SIGNATURE,
|
||||
response.getHttpHeaders().getHeader(REQUEST_ID), plainBody(response)));
|
||||
if (Strings.isEmpty(signature)) {
|
||||
throw new ValidationException(String.format("校验失败, 签名[%s]不存在, Request-Id=%s, HttpResponseBody=%s",
|
||||
SIGNATURE, response.getHttpHeaders().getHeader(REQUEST_ID), decryptResponseBodyIfNecessary(response)));
|
||||
}
|
||||
|
||||
// 签名证书编号
|
||||
String serialNumber = response.getHttpHeaders().getHeader(BANK_CERTIFICATE_SERIAL);
|
||||
if (!verifier.verify(serialNumber, message, signature)) {
|
||||
throw new ValidationException(String.format("校验失败, 签名[%s=%s]校验未通过, Request-Id=%s, HttpResponseBody=%s", SIGNATURE,
|
||||
signature, response.getHttpHeaders().getHeader(REQUEST_ID), plainBody(response)));
|
||||
throw new ValidationException(String.format("校验失败, 签名[%s=%s]校验未通过, Request-Id=%s, HttpResponseBody=%s",
|
||||
SIGNATURE, signature, response.getHttpHeaders().getHeader(REQUEST_ID), decryptResponseBodyIfNecessary(response)));
|
||||
}
|
||||
}
|
||||
|
||||
private String plainBody(OriginalResponse originalResponse) {
|
||||
byte[] body = originalResponse.getBody();
|
||||
private String decryptResponseBodyIfNecessary(OriginalResponse originalResponse) {
|
||||
// 原始应答报文
|
||||
byte[] responseBody = originalResponse.getBody();
|
||||
|
||||
// 判断是否有body加密密钥
|
||||
if (!originalResponse.getHttpHeaders().hasHeader(SECRET_KEY)) {
|
||||
return Strings.toStr(body);
|
||||
// 明文直接返回
|
||||
return Strings.toStr(responseBody);
|
||||
}
|
||||
|
||||
// 解密密钥
|
||||
// SecretKey
|
||||
String secretKey = originalResponse.getHttpHeaders().getHeader(SECRET_KEY);
|
||||
// 解密密钥
|
||||
String decryptSecretKey = getProfile().getPrivacy().getDecryptor().decrypt(secretKey);
|
||||
// 解密body
|
||||
return getProfile().getPrivacy().getSecretCipher().decrypt(Strings.toBytes(decryptSecretKey), body);
|
||||
return getProfile().getPrivacy().getSecretCipher().decrypt(Strings.toBytes(decryptSecretKey), responseBody);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
package com.czcb.scfs.api.core;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class ConstantsTest {
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
Assertions.assertEquals("X-SCFS-Channel-Serial", Constants.CHANNEL_CERTIFICATE_SERIAL);
|
||||
Assertions.assertEquals("X-SCFS-Channel-No", Constants.CHANNEL_NO);
|
||||
Assertions.assertEquals("X-SCFS-App-No", Constants.APP_NO);
|
||||
Assertions.assertEquals("X-SCFS-Signature", Constants.SIGNATURE);
|
||||
Assertions.assertEquals("X-SCFS-Api-Version", Constants.API_VERSION);
|
||||
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-Request-Id", Constants.REQUEST_ID);
|
||||
|
||||
Assertions.assertEquals("Content-Type", Constants.CONTENT_TYPE);
|
||||
Assertions.assertEquals("Authorization", Constants.AUTHORIZATION);
|
||||
Assertions.assertEquals("Accept-Encoding", Constants.ACCEPT_ENCODING);
|
||||
Assertions.assertEquals("gzip", Constants.GZIP_ENCODING);
|
||||
Assertions.assertEquals("Content-Encoding", Constants.CONTENT_ENCODING);
|
||||
Assertions.assertEquals("User-Agent", Constants.USER_AGENT);
|
||||
|
||||
Assertions.assertEquals(16, Constants.HEX);
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ import com.czcb.scfs.api.core.http.client.TestVerifier;
|
|||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -23,6 +24,7 @@ import java.util.Map;
|
|||
import static com.czcb.scfs.api.core.Constants.TIMESTAMP;
|
||||
|
||||
class DefaultValidatorTest {
|
||||
// 创建配置文件
|
||||
Profile buildProfile() {
|
||||
PrivateKey privateKey = KeyText.loadTestPrivateKeyRSA();
|
||||
X509Certificate certificate = KeyText.loadTestRSA();
|
||||
|
|
@ -83,9 +85,51 @@ class DefaultValidatorTest {
|
|||
.statusCode(500)
|
||||
.build();
|
||||
|
||||
Assertions.assertThrows(ValidationException.class, () -> {
|
||||
defaultValidator.isInvalidHttpCode(response);
|
||||
});
|
||||
ValidationException validationException = Assertions.assertThrows(ValidationException.class, () -> defaultValidator.isInvalidHttpCode(response));
|
||||
Assertions.assertEquals("校验失败, HttpStatusCode=500, Request-Id=123456789, HttpResponseBody=123", validationException.getLocalizedMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void isInvalidHttpCodeError1xx() {
|
||||
DefaultValidator defaultValidator = new DefaultValidator(buildProfile());
|
||||
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("X-SCFS-Request-Id", "123456789");
|
||||
OriginalResponse response = new OriginalResponse.Builder()
|
||||
.request(new HttpRequest.Builder()
|
||||
.httpMethod(HttpMethod.POST)
|
||||
.url("http://demo")
|
||||
.build())
|
||||
.body("123")
|
||||
.headers(headers)
|
||||
.statusCode(100)
|
||||
.build();
|
||||
|
||||
ValidationException validationException = Assertions.assertThrows(ValidationException.class, () -> defaultValidator.isInvalidHttpCode(response));
|
||||
Assertions.assertEquals("校验失败, HttpStatusCode=100, Request-Id=123456789, HttpResponseBody=123", validationException.getLocalizedMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void isInvalidHttpCodeError1xxBody() {
|
||||
DefaultValidator defaultValidator = new DefaultValidator(buildProfile());
|
||||
|
||||
String plain = "0987654321123456";
|
||||
String secretKey = defaultValidator.getProfile().getPrivacy().getEncryptor().encrypt(plain);
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("X-SCFS-Request-Id", "123456789");
|
||||
headers.put("X-SCFS-Secret-Key", secretKey);
|
||||
OriginalResponse response = new OriginalResponse.Builder()
|
||||
.request(new HttpRequest.Builder()
|
||||
.httpMethod(HttpMethod.POST)
|
||||
.url("http://demo")
|
||||
.build())
|
||||
.body(defaultValidator.getProfile().getPrivacy().getSecretCipher().encrypt(plain.getBytes(StandardCharsets.UTF_8), "123".getBytes(StandardCharsets.UTF_8)))
|
||||
.headers(headers)
|
||||
.statusCode(100)
|
||||
.build();
|
||||
|
||||
ValidationException validationException = Assertions.assertThrows(ValidationException.class, () -> defaultValidator.isInvalidHttpCode(response));
|
||||
Assertions.assertEquals("校验失败, HttpStatusCode=100, Request-Id=123456789, HttpResponseBody=123", validationException.getLocalizedMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -108,8 +152,9 @@ class DefaultValidatorTest {
|
|||
defaultValidator.validateTimestamp(response);
|
||||
}
|
||||
|
||||
// 没有时间戳案例
|
||||
@Test
|
||||
void validateTimestampError() {
|
||||
void validateTimestampNoneError() {
|
||||
DefaultValidator defaultValidator = new DefaultValidator(buildProfile());
|
||||
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
|
|
@ -124,9 +169,30 @@ class DefaultValidatorTest {
|
|||
.statusCode(500)
|
||||
.build();
|
||||
|
||||
Assertions.assertThrows(TimestampException.class, () -> {
|
||||
defaultValidator.validateTimestamp(response);
|
||||
});
|
||||
TimestampException validationException = Assertions.assertThrows(TimestampException.class, () -> defaultValidator.validateTimestamp(response));
|
||||
Assertions.assertEquals("校验失败, 时间戳[X-SCFS-Timestamp]不存在, Request-Id=123456789, HttpResponseBody=123", validationException.getLocalizedMessage());
|
||||
}
|
||||
|
||||
// 时间戳错误案例
|
||||
@Test
|
||||
void validateTimestampError() {
|
||||
DefaultValidator defaultValidator = new DefaultValidator(buildProfile());
|
||||
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("X-SCFS-Request-Id", "123456789");
|
||||
headers.put("X-SCFS-Timestamp", "1716941850");
|
||||
OriginalResponse response = new OriginalResponse.Builder()
|
||||
.request(new HttpRequest.Builder()
|
||||
.httpMethod(HttpMethod.POST)
|
||||
.url("http://demo")
|
||||
.build())
|
||||
.body("123")
|
||||
.headers(headers)
|
||||
.statusCode(200)
|
||||
.build();
|
||||
|
||||
TimestampException validationException = Assertions.assertThrows(TimestampException.class, () -> defaultValidator.validateTimestamp(response));
|
||||
Assertions.assertEquals("校验失败, 时间戳[X-SCFS-Timestamp=1716941850]已过期, Request-Id=123456789, HttpResponseBody=123", validationException.getLocalizedMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -142,14 +208,77 @@ class DefaultValidatorTest {
|
|||
.build())
|
||||
.body("123")
|
||||
.headers(headers)
|
||||
.statusCode(500)
|
||||
.statusCode(200)
|
||||
.build();
|
||||
|
||||
Assertions.assertThrows(ValidationException.class, () -> {
|
||||
defaultValidator.validateResponseSignature(response, new DefaultChannel.Builder()
|
||||
.channelNo("1010")
|
||||
.appNo("1111")
|
||||
.build());
|
||||
});
|
||||
ValidationException validationException = Assertions.assertThrows(ValidationException.class, () -> defaultValidator.validateResponseSignature(response, new DefaultChannel.Builder()
|
||||
.channelNo("1010")
|
||||
.appNo("1111")
|
||||
.build()));
|
||||
Assertions.assertEquals("校验失败, 签名[X-SCFS-Signature]不存在, Request-Id=123456789, HttpResponseBody=123", validationException.getLocalizedMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void validateResponseSignatureError() {
|
||||
DefaultValidator defaultValidator = new DefaultValidator(buildProfile());
|
||||
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("X-SCFS-Request-Id", "123456789");
|
||||
headers.put("X-SCFS-Signature", "MTIzNDU2Nzg5");
|
||||
headers.put("X-SCFS-Serial", "6CDDAA92CAD75998325027647847330C1756291");
|
||||
OriginalResponse response = new OriginalResponse.Builder()
|
||||
.request(new HttpRequest.Builder()
|
||||
.httpMethod(HttpMethod.POST)
|
||||
.url("http://demo")
|
||||
.build())
|
||||
.body("123")
|
||||
.headers(headers)
|
||||
.statusCode(200)
|
||||
.build();
|
||||
|
||||
ValidationException validationException = Assertions.assertThrows(ValidationException.class, () -> defaultValidator.validateResponseSignature(response, new DefaultChannel.Builder()
|
||||
.channelNo("1010")
|
||||
.appNo("1111")
|
||||
.build()));
|
||||
Assertions.assertEquals("校验失败, 签名[X-SCFS-Signature=MTIzNDU2Nzg5]校验未通过, Request-Id=123456789, HttpResponseBody=123", validationException.getLocalizedMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void validate() {
|
||||
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
|
||||
String message = String.format("X-SCFS-Nonce=3333333333,X-SCFS-Timestamp=%s,X-SCFS-Serial=6CDDAA92CAD75998325027647847330C1756291,X-SCFS-Channel-Serial=6CDDAA92CAD75998325027647847330C1756291,X-SCFS-Secret-Key=ddd\n" +
|
||||
"123\n", timestamp);
|
||||
|
||||
DefaultValidator defaultValidator = new DefaultValidator(buildProfile());
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("X-SCFS-Request-Id", "123456789");
|
||||
headers.put("X-SCFS-Timestamp", timestamp);
|
||||
headers.put("X-SCFS-Nonce", "3333333333");
|
||||
headers.put("X-SCFS-Secret-Key", "ddd");
|
||||
headers.put("X-SCFS-Signature", defaultValidator.getProfile().getSignature().getSigner().sign(message).getSignature());
|
||||
headers.put("X-SCFS-Serial", "6CDDAA92CAD75998325027647847330C1756291");
|
||||
headers.put("X-SCFS-Channel-Serial", "6CDDAA92CAD75998325027647847330C1756291");
|
||||
|
||||
OriginalResponse response = new OriginalResponse.Builder()
|
||||
.request(new HttpRequest.Builder()
|
||||
.httpMethod(HttpMethod.POST)
|
||||
.url("http://demo")
|
||||
.build())
|
||||
.body("123")
|
||||
.headers(headers)
|
||||
.statusCode(200)
|
||||
.build();
|
||||
|
||||
Assertions.assertDoesNotThrow(() -> defaultValidator.validate(null, response, new DefaultChannel.Builder()
|
||||
.channelNo("1010")
|
||||
.appNo("1111")
|
||||
.build()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getProfile() {
|
||||
DefaultValidator defaultValidator = new DefaultValidator(buildProfile());
|
||||
Assertions.assertNotNull(defaultValidator.getProfile());
|
||||
Assertions.assertEquals("000000", defaultValidator.getProfile().getChannel().getChannelNo());
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue