diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/DefaultValidator.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/DefaultValidator.java index 9b1c853..07310dd 100644 --- a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/DefaultValidator.java +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/DefaultValidator.java @@ -4,9 +4,7 @@ import com.czcb.scfs.api.core.Channel; import com.czcb.scfs.api.core.Profile; import com.czcb.scfs.api.core.exception.TimestampException; import com.czcb.scfs.api.core.exception.ValidationException; -import com.czcb.scfs.api.core.http.HttpLogger; import com.czcb.scfs.api.core.http.HttpRequest; -import com.czcb.scfs.api.core.http.LogLevel; import com.czcb.scfs.api.core.http.OriginalResponse; import com.czcb.scfs.api.core.util.Strings; @@ -25,72 +23,61 @@ public final class DefaultValidator implements Validator { // 时间戳过期时间 private static final int RESPONSE_EXPIRED_MINUTES = 10; private final Verifier verifier; - private final HttpLogger httpLogger; private final Profile profile; public DefaultValidator(Profile profile) { this.profile = profile; this.verifier = profile.getSignature().getVerifier(); - this.httpLogger = new HttpLogger(profile.getHttpProfile() == null ? LogLevel.basic : profile.getHttpProfile().logLevel()); - } - - private boolean isInvalidHttpCode(int httpCode) { - return httpCode < HTTP_OK || httpCode >= HTTP_MULT_CHOICE; - } - - private void validateTimestamp(HttpRequest request, OriginalResponse response) { - try { - String timestamp = response.getHttpHeaders().getHeader(TIMESTAMP); - if (Strings.isEmpty(timestamp)) { - throw new TimestampException(String.format("响应头时间戳校验失败, 响应头[%s=%s]不存在, Request-Id=%s", TIMESTAMP, - timestamp, response.getHttpHeaders().getHeader(REQUEST_ID))); - } - - 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", TIMESTAMP, - timestamp, response.getHttpHeaders().getHeader(REQUEST_ID))); - } - } catch (Exception e) { - httpLogger.logResponseError(request, response); - throw new TimestampException(e.getMessage(), e); - } } @Override public void validate(HttpRequest request, OriginalResponse response, Channel channel) { + // 校验应答状态码 + isInvalidHttpCode(response); + // 校验时间戳 validateTimestamp(request, response); - if (!headerContainsSecretKey(response)) { - return; + // 校验应答签名 + validateResponseSignature(request, response, channel); + } + + private 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), Strings.toStr(response.getBody()))); + } + } + + public void validateTimestamp(HttpRequest request, 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), Strings.toStr(response.getBody()))); } + 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), Strings.toStr(response.getBody()))); + } + } + + public void validateResponseSignature(HttpRequest request, OriginalResponse response, Channel channel) { // 待签名串 String message = profile.getSignature().getCredential().buildResponseMessage(response, channel); - try { - String signature = response.getHttpHeaders().getHeader(SIGNATURE); - if (signature == null || signature.isEmpty()) { - throw new ValidationException(String.format("响应校验失败, 签名不存在, Request-Id=%s", - response.getHttpHeaders().getHeader(REQUEST_ID))); - } + 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), Strings.toStr(response.getBody()))); + } - // 签名证书编号 - String serialNumber = response.getHttpHeaders().getHeader(BANK_CERTIFICATE_SERIAL); - if (!verifier.verify(serialNumber, message, signature)) { - httpLogger.logResponseError(request, response); - throw new ValidationException(String.format("响应校验失败, 签名校验未通过, Request-Id=%s", - response.getHttpHeaders().getHeader(REQUEST_ID))); - } - } catch (Exception e) { - throw new ValidationException(e.getMessage(), e); + // 签名证书编号 + 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), Strings.toStr(response.getBody()))); } } - - private boolean headerContainsSecretKey(OriginalResponse response) { - return Strings.isNotEmpty(response.getHttpHeaders().getHeader(SECRET_KEY)) - && Strings.isNotEmpty(response.getHttpHeaders().getHeader(CHANNEL_CERTIFICATE_SERIAL)) - && Strings.isNotEmpty(response.getHttpHeaders().getHeader(BANK_CERTIFICATE_SERIAL)); - } } 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 b99c0e6..8da22ca 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 @@ -4,8 +4,6 @@ import com.czcb.scfs.api.core.ApiClient; import com.czcb.scfs.api.core.Channel; import com.czcb.scfs.api.core.Profile; import com.czcb.scfs.api.core.cipher.*; -import com.czcb.scfs.api.core.exception.ApiClientException; -import com.czcb.scfs.api.core.util.StopWatch; import com.czcb.scfs.api.core.util.Strings; import java.util.Objects; @@ -58,18 +56,8 @@ public abstract class AbstractApiClient implements ApiClient { // 打印请求数据 httpLogger.logRequest(newRequest, getHttpVersion()); - // 启动计时器 - StopWatch watch = new StopWatch("scfs-request").start(); - OriginalResponse originalResponse; - try { - // 实际调用 - originalResponse = doRemoteExecute(newRequest); - } catch (Exception e) { - httpLogger.logDoRemoteExecuteError(newRequest, e); - throw new ApiClientException(e); - } finally { - watch.stop(); - } + // 真实调用 + OriginalResponse originalResponse = doRemoteExecute(newRequest); // 校验响应 OriginalResponse resultfulResponse = validateResponse(newRequest, originalResponse); @@ -77,7 +65,7 @@ public abstract class AbstractApiClient implements ApiClient { // 解析返回数据 HttpResponse httpResponse = assembleHttpResponse(resultfulResponse, responseClass); // 打印响应结果 - httpLogger.logResponse(newRequest, originalResponse, httpResponse, watch.getLastTaskTimeMillis()); + httpLogger.logResponse(newRequest, originalResponse, httpResponse); return httpResponse; } diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/HttpLogger.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/HttpLogger.java index 81a50c7..4cbf63f 100644 --- a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/HttpLogger.java +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/HttpLogger.java @@ -59,10 +59,9 @@ public class HttpLogger { * * @param originalResponse 响应数据 * @param response 响应数据解析后数据 - * @param duration 请求耗时 * @param 返回类型 */ - protected void logResponse(HttpRequest request, OriginalResponse originalResponse, HttpResponse response, long duration) { + protected void logResponse(HttpRequest request, OriginalResponse originalResponse, HttpResponse response) { String logPrefixText = logPrefix(request); if (isFull(logLevel)) { String reasonPhrase = httpReasonPhrase(originalResponse); @@ -79,10 +78,6 @@ public class HttpLogger { String text = Json.toJson(response.getServiceResponse()); logger.info("{}应答报文:{}", logPrefixText, text); - - if (isFull(logLevel)) { - logger.info("{}请求-应答耗时(毫秒):{}", logPrefixText, duration); - } } /** @@ -90,15 +85,14 @@ public class HttpLogger { * * @param originalResponse 响应数据 */ - public void logResponseError(HttpRequest request, OriginalResponse originalResponse) { + public void logResponseError(HttpRequest request, OriginalResponse originalResponse, String errorMessage) { String logPrefixText = logPrefix(request); - logger.error("{}响应:{} {}", logPrefixText, originalResponse.getVersion(), originalResponse.getStatusCode()); if (isFull(logLevel)) { - originalResponse.getHttpHeaders().getHeaders().forEach((k, v) -> logger.info("{}请求头:{}:{}", logPrefixText, k, v)); - - String body = Strings.toStr(originalResponse.getBody()); - logger.error("{}响应原始报文:{}", logPrefixText, body); + originalResponse.getHttpHeaders().getHeaders().forEach((k, v) -> logger.info("{}应答头:{}:{}", logPrefixText, k, v)); } + + String body = Strings.toStr(originalResponse.getBody() == null ? new byte[]{} : originalResponse.getBody()); + logger.error("{}{}, 应答原始报文:{}", logPrefixText, errorMessage, body); } public String httpReasonPhrase(OriginalResponse response) { @@ -110,11 +104,6 @@ public class HttpLogger { return status.getReasonPhrase(); } - public void logRemoteError(HttpRequest newRequest, String message) { - String logPrefixText = logPrefix(newRequest); - logger.error("{}远程调用异常:{}", logPrefixText, message); - } - private String logPrefix(HttpRequest request) { if (logPrefix) { return String.format("[%s] ", request.getId()); @@ -122,4 +111,5 @@ public class HttpLogger { return ""; } + } diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/client/ApacheHttpclient.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/client/ApacheHttpclient.java index c2e78ea..d5a7388 100644 --- a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/client/ApacheHttpclient.java +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/client/ApacheHttpclient.java @@ -41,9 +41,13 @@ public class ApacheHttpclient extends AbstractApiClient { } @Override - protected OriginalResponse doRemoteExecute(HttpRequest httpRequest) throws IOException { - ClassicHttpRequest request = buildHttpRequest(httpRequest); - return httpClient.execute(request, response -> assembleOriginalResponse(httpRequest, response)); + protected OriginalResponse doRemoteExecute(HttpRequest httpRequest) { + try { + ClassicHttpRequest request = buildHttpRequest(httpRequest); + return httpClient.execute(request, response -> assembleOriginalResponse(httpRequest, response)); + } catch (Exception e) { + throw new ApiClientException(String.format("服务调用异常: %s", e.getMessage()), e); + } } @Override diff --git a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/cipher/DefaultValidatorTest.java b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/cipher/DefaultValidatorTest.java index 11b52b6..f1d2446 100644 --- a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/cipher/DefaultValidatorTest.java +++ b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/cipher/DefaultValidatorTest.java @@ -1,8 +1,14 @@ -//package com.czcb.scfs.api.core.cipher; -// -//class DefaultValidatorTest { -//// -//// @Test -//// void validate() { -//// } -//} \ No newline at end of file +package com.czcb.scfs.api.core.cipher; + +import org.junit.jupiter.api.Test; + +class DefaultValidatorTest { + + @Test + void validate() { + } + + @Test + void responseHeaderContainsSecretKey() { + } +} \ No newline at end of file diff --git a/scfs-api-service/src/main/java/com/czcb/scfs/api/service/v2/bmd/BmdService.java b/scfs-api-service/src/main/java/com/czcb/scfs/api/service/v2/bmd/BmdService.java index 1d35170..f8d30d5 100644 --- a/scfs-api-service/src/main/java/com/czcb/scfs/api/service/v2/bmd/BmdService.java +++ b/scfs-api-service/src/main/java/com/czcb/scfs/api/service/v2/bmd/BmdService.java @@ -5,7 +5,8 @@ import com.czcb.scfs.api.core.http.HttpHeaders; import com.czcb.scfs.api.core.http.HttpResponse; import com.czcb.scfs.api.service.v2.bmd.model.*; -import static com.czcb.scfs.api.core.Constants.*; +import static com.czcb.scfs.api.core.Constants.API_VERSION; +import static com.czcb.scfs.api.core.Constants.V_2; /** * @author wangwei diff --git a/scfs-api-service/src/main/java/com/czcb/scfs/api/service/v2/face/FaceService.java b/scfs-api-service/src/main/java/com/czcb/scfs/api/service/v2/face/FaceService.java index cd8df9c..244d0d8 100644 --- a/scfs-api-service/src/main/java/com/czcb/scfs/api/service/v2/face/FaceService.java +++ b/scfs-api-service/src/main/java/com/czcb/scfs/api/service/v2/face/FaceService.java @@ -6,7 +6,8 @@ import com.czcb.scfs.api.core.http.HttpResponse; import com.czcb.scfs.api.service.v2.face.model.FaceFileRequest; import com.czcb.scfs.api.service.v2.face.model.FaceFileResponse; -import static com.czcb.scfs.api.core.Constants.*; +import static com.czcb.scfs.api.core.Constants.API_VERSION; +import static com.czcb.scfs.api.core.Constants.V_2; /** * @author wangwei diff --git a/scfs-api-service/src/main/java/com/czcb/scfs/api/service/v2/file/FileService.java b/scfs-api-service/src/main/java/com/czcb/scfs/api/service/v2/file/FileService.java index 429372d..b24d02a 100644 --- a/scfs-api-service/src/main/java/com/czcb/scfs/api/service/v2/file/FileService.java +++ b/scfs-api-service/src/main/java/com/czcb/scfs/api/service/v2/file/FileService.java @@ -8,7 +8,8 @@ import com.czcb.scfs.api.service.v2.file.model.DownloadFileResponse; import com.czcb.scfs.api.service.v2.file.model.UploadFileRequest; import com.czcb.scfs.api.service.v2.file.model.UploadFileResponse; -import static com.czcb.scfs.api.core.Constants.*; +import static com.czcb.scfs.api.core.Constants.API_VERSION; +import static com.czcb.scfs.api.core.Constants.V_2; /** * 文件下载 diff --git a/scfs-api-service/src/main/java/com/czcb/scfs/api/service/v2/ocr/OcrService.java b/scfs-api-service/src/main/java/com/czcb/scfs/api/service/v2/ocr/OcrService.java index b760549..ff1b6d2 100644 --- a/scfs-api-service/src/main/java/com/czcb/scfs/api/service/v2/ocr/OcrService.java +++ b/scfs-api-service/src/main/java/com/czcb/scfs/api/service/v2/ocr/OcrService.java @@ -6,7 +6,8 @@ import com.czcb.scfs.api.core.http.HttpResponse; import com.czcb.scfs.api.service.v2.ocr.model.OcrFileRequest; import com.czcb.scfs.api.service.v2.ocr.model.OcrFileResponse; -import static com.czcb.scfs.api.core.Constants.*; +import static com.czcb.scfs.api.core.Constants.API_VERSION; +import static com.czcb.scfs.api.core.Constants.V_2; /** * @author wangwei diff --git a/scfs-api-service/src/main/java/com/czcb/scfs/api/service/v2/pay/model/EntPayAgrtQueryListResponse.java b/scfs-api-service/src/main/java/com/czcb/scfs/api/service/v2/pay/model/EntPayAgrtQueryListResponse.java index 45fae51..9ea007d 100644 --- a/scfs-api-service/src/main/java/com/czcb/scfs/api/service/v2/pay/model/EntPayAgrtQueryListResponse.java +++ b/scfs-api-service/src/main/java/com/czcb/scfs/api/service/v2/pay/model/EntPayAgrtQueryListResponse.java @@ -33,7 +33,7 @@ public class EntPayAgrtQueryListResponse implements ApiResponse { */ @SerializedName("sys_serial_no") private String sysSerialNo; - + /** * 受托支付协议列表 */ diff --git a/scfs-api-service/src/main/java/com/czcb/scfs/api/service/v2/sms/SmsService.java b/scfs-api-service/src/main/java/com/czcb/scfs/api/service/v2/sms/SmsService.java index 067c7a3..125c425 100644 --- a/scfs-api-service/src/main/java/com/czcb/scfs/api/service/v2/sms/SmsService.java +++ b/scfs-api-service/src/main/java/com/czcb/scfs/api/service/v2/sms/SmsService.java @@ -6,7 +6,8 @@ import com.czcb.scfs.api.core.http.HttpResponse; import com.czcb.scfs.api.service.v2.sms.model.SendVerifySignRequest; import com.czcb.scfs.api.service.v2.sms.model.SendVerifySignResponse; -import static com.czcb.scfs.api.core.Constants.*; +import static com.czcb.scfs.api.core.Constants.API_VERSION; +import static com.czcb.scfs.api.core.Constants.V_2; /** * 短信服务