feat: 发号器

main
13009 2024-07-26 17:29:14 +08:00
parent da1b7b0351
commit f1a3762154
26 changed files with 272 additions and 47 deletions

View File

@ -19,9 +19,11 @@ import static java.util.Objects.requireNonNull;
*/
public class DefaultCredential implements Credential {
private final Signer signer;
private final Sequencer sequencer;
public DefaultCredential(Signer signer) {
public DefaultCredential(Signer signer, Sequencer sequencer) {
this.signer = requireNonNull(signer);
this.sequencer = requireNonNull(sequencer);
}
@Override
@ -34,7 +36,7 @@ public class DefaultCredential implements Credential {
private void addNecessityCustomizeHeaders(HttpRequest request, Channel channel, Signer signer) {
request.getHttpHeaders().addHeader(CHANNEL_CERTIFICATE_SERIAL, signer.getCertificateSerial());
request.getHttpHeaders().addHeader(NONCE, request.getId());
request.getHttpHeaders().addHeader(NONCE, Objects.isNull(request.getId()) ? sequencer.next() : request.getId());
request.getHttpHeaders().addHeader(TIMESTAMP, DateTimes.ofTimestamp());
if (channel.isChannelGroupMode()) {

View File

@ -0,0 +1,37 @@
package com.czcb.scfs.api.core.cipher;
import com.czcb.scfs.api.core.util.DateTimes;
import java.security.SecureRandom;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author wangwei
* @since 2024/7/26
*/
public class DefaultSequencer implements Sequencer {
private final SecureRandom secureRandom = new SecureRandom();
private final AtomicInteger number = new AtomicInteger(1);
@Override
public String next() {
return String.format("%s%s%s", getCurrentDate(), getRandomStr(), getNumber());
}
private String getCurrentDate() {
return DateTimes.of("yyyyMMdd");
}
private String getRandomStr() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 16; i++) {
sb.append(secureRandom.nextInt(9));
}
return sb.toString();
}
private Integer getNumber() {
return number.getAndIncrement();
}
}

View File

@ -11,12 +11,14 @@ public class DefaultSignature implements Signature {
private final Credential credential;
private final Signer signer;
private final Verifier verifier;
private final Sequencer sequencer;
public DefaultSignature(CertificateProvider certificateProvider, Signer signer, Verifier verifier) {
public DefaultSignature(CertificateProvider certificateProvider, Signer signer, Verifier verifier, Sequencer sequencer) {
this.certificateProvider = certificateProvider;
this.signer = signer;
this.verifier = verifier;
this.credential = new DefaultCredential(this.signer);
this.credential = new DefaultCredential(signer, sequencer);
this.sequencer = sequencer;
}
@Override
@ -38,4 +40,9 @@ public class DefaultSignature implements Signature {
public Verifier getVerifier() {
return verifier;
}
@Override
public Sequencer getSequencer() {
return sequencer;
}
}

View File

@ -0,0 +1,15 @@
package com.czcb.scfs.api.core.cipher;
import com.czcb.scfs.api.core.validation.Length;
/**
*
*
* @author wangwei
* @since 2024/7/26
*/
public interface Sequencer {
@Length(max = 32)
String next();
}

View File

@ -28,4 +28,11 @@ public interface Signature {
* @return Verifier
*/
Verifier getVerifier();
/**
*
*
* @return Sequencer
*/
Sequencer getSequencer();
}

View File

@ -1,8 +1,5 @@
package com.czcb.scfs.api.core.http;
import com.czcb.scfs.api.core.util.Nonce;
import com.czcb.scfs.api.core.util.Strings;
import java.util.Map;
import java.util.Objects;
@ -193,10 +190,6 @@ public class HttpRequest {
requireNonNull(this.httpMethod);
requireNonNull(this.url);
if (Strings.isEmpty(this.id)) {
this.id = Nonce.ofNonce();
}
if (Objects.isNull(this.headers)) {
this.headers = new HttpHeaders();
}

View File

@ -0,0 +1,24 @@
package com.czcb.scfs.api.core.validation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* @author wangwei
* @since 2024/7/9
*/
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Documented
public @interface Length {
String message() default "";
int min() default 0;
int max() default 100;
}

View File

@ -0,0 +1,26 @@
package com.czcb.scfs.api.core.cipher;
import com.czcb.scfs.api.core.util.DateTimes;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
class DefaultSequencerTest {
@Test
void next() {
Sequencer sequencer = new DefaultSequencer();
String next = sequencer.next();
Assertions.assertNotNull(next);
Assertions.assertEquals(25, next.length());
}
@Test
void testNextDate() {
Sequencer sequencer = new DefaultSequencer();
String next = sequencer.next();
Assertions.assertNotNull(next);
String text = DateTimes.ofTransDate().replace("-", "");
Assertions.assertTrue(next.startsWith(text));
}
}

View File

@ -20,11 +20,13 @@ class DefaultSignatureTest {
Signer signer = new TestSigner(KeyText.loadTestPrivateKeyRSA(), "");
Verifier verifier = new TestVerifier(provider);
DefaultSignature signature = new DefaultSignature(provider, signer, verifier);
DefaultSignature signature = new DefaultSignature(provider, signer, verifier, new DefaultSequencer());
Assertions.assertNotNull(signature.getCertificateProvider());
Assertions.assertNotNull(signature.getSigner());
Assertions.assertNotNull(signature.getCredential());
Assertions.assertNotNull(signature.getVerifier());
Assertions.assertNotNull(signature.getSequencer());
Assertions.assertNotNull(signature.getSequencer().next());
}
}

View File

@ -35,7 +35,7 @@ class DefaultValidatorTest {
Privacy privacy = new TestPrivacy(privateKey, certificateProvider);
Signature signature = new DefaultSignature(certificateProvider,
new TestSigner(privateKey, "6CDDAA92CAD75998325027647847330C1756291"),
new TestVerifier(certificateProvider));
new TestVerifier(certificateProvider), new DefaultSequencer());
return new TestProfile(
privacy,
@ -62,7 +62,7 @@ class DefaultValidatorTest {
Privacy privacy = new TestPrivacy(privateKey, certificateProvider);
Signature signature = new DefaultSignature(certificateProvider,
new TestSigner(privateKey, "823CF3E310F2E2ED1AF85506E74A95DC4301006FDEF2FD019953FAF4DE12A8BF"),
new TestVerifier(certificateProvider));
new TestVerifier(certificateProvider), new DefaultSequencer());
return new TestProfile(
privacy,

View File

@ -29,7 +29,10 @@ class ApiClientBuilderTest {
list.add(certificate);
CertificateProvider certificateProvider = new LocalCertificateProvider(list);
Privacy privacy = new TestPrivacy(privateKey, certificateProvider);
Signature signature = new DefaultSignature(certificateProvider, new TestSigner(privateKey, ""), new TestVerifier(certificateProvider));
Signature signature = new DefaultSignature(certificateProvider,
new TestSigner(privateKey, ""),
new TestVerifier(certificateProvider),
new DefaultSequencer());
return new TestProfile(
privacy,
@ -106,7 +109,10 @@ class ApiClientBuilderTest {
list.add(certificate);
CertificateProvider certificateProvider = new LocalCertificateProvider(list);
Privacy privacy = new TestPrivacy(privateKey, certificateProvider);
Signature signature = new DefaultSignature(certificateProvider, new TestSigner(privateKey, ""), new TestVerifier(certificateProvider));
Signature signature = new DefaultSignature(certificateProvider,
new TestSigner(privateKey, ""),
new TestVerifier(certificateProvider),
new DefaultSequencer());
return new TestProfile(
privacy,
@ -126,7 +132,10 @@ class ApiClientBuilderTest {
list.add(certificate);
CertificateProvider certificateProvider = new LocalCertificateProvider(list);
Privacy privacy = new TestPrivacy(privateKey, certificateProvider);
Signature signature = new DefaultSignature(certificateProvider, new TestSigner(privateKey, ""), new TestVerifier(certificateProvider));
Signature signature = new DefaultSignature(certificateProvider,
new TestSigner(privateKey, ""),
new TestVerifier(certificateProvider),
new DefaultSequencer());
return new TestProfile(
privacy,

View File

@ -42,6 +42,13 @@ class HttpRequestTest {
.url("/aa")
.build();
Assertions.assertNotNull(httpRequest.getHttpHeaders());
httpRequest = new HttpRequest.Builder()
.httpMethod(HttpMethod.GET)
.url("/aa")
.headers(null)
.build();
Assertions.assertNotNull(httpRequest.getHttpHeaders());
}
@Test
@ -85,6 +92,18 @@ class HttpRequestTest {
Assertions.assertNull(httpRequest.getHttpHeaders().getHeader("Accept-Encoding"));
}
@Test
void id() {
HttpRequest httpRequest = new HttpRequest.Builder()
.httpMethod(HttpMethod.GET)
.url("/aa")
.id("3333")
.compression(true)
.build();
Assertions.assertEquals("3333", httpRequest.getId());
}
@Test
void newBuilder() {
HttpRequest httpRequest = new HttpRequest.Builder()

View File

@ -46,7 +46,8 @@ class ApacheHttpclientGroupTest {
Privacy privacy = new TestPrivacy(privateKey, certificateProvider);
Signature signature = new DefaultSignature(certificateProvider,
new TestSigner(privateKey, "6CDDAA92CAD75998325027647847330C1756291"),
new TestVerifier(certificateProvider));
new TestVerifier(certificateProvider),
new DefaultSequencer());
return new TestProfile(
privacy, signature, new DefaultChannel.Builder()

View File

@ -45,7 +45,8 @@ class ApacheHttpclientProxyTest {
Privacy privacy = new TestPrivacy(privateKey, certificateProvider);
Signature signature = new DefaultSignature(certificateProvider,
new TestSigner(privateKey, "6CDDAA92CAD75998325027647847330C1756291"),
new TestVerifier(certificateProvider));
new TestVerifier(certificateProvider),
new DefaultSequencer());
return new TestProfile(
privacy, signature, new DefaultChannel.Builder()

View File

@ -50,7 +50,8 @@ class ApacheHttpclientTest {
Privacy privacy = new TestPrivacy(privateKey, certificateProvider);
Signature signature = new DefaultSignature(certificateProvider,
new TestSigner(privateKey, "6CDDAA92CAD75998325027647847330C1756291"),
new TestVerifier(certificateProvider));
new TestVerifier(certificateProvider),
new DefaultSequencer());
return new TestProfile(
privacy, signature, new DefaultChannel.Builder()

View File

@ -45,7 +45,8 @@ class ApacheHttpclientTestProxyTest {
Privacy privacy = new TestPrivacy(privateKey, certificateProvider);
Signature signature = new DefaultSignature(certificateProvider,
new TestSigner(privateKey, "6CDDAA92CAD75998325027647847330C1756291"),
new TestVerifier(certificateProvider));
new TestVerifier(certificateProvider),
new DefaultSequencer());
return new TestProfile(
privacy, signature, new DefaultChannel.Builder()

View File

@ -46,7 +46,8 @@ class ApacheHttpclientV2Test {
Privacy privacy = new TestPrivacy(privateKey, certificateProvider);
Signature signature = new DefaultSignature(certificateProvider,
new TestSigner(privateKey, "6CDDAA92CAD75998325027647847330C1756291"),
new TestVerifier(certificateProvider));
new TestVerifier(certificateProvider),
new DefaultSequencer());
return new TestProfile(
privacy, signature, new DefaultChannel.Builder()

View File

@ -46,7 +46,8 @@ class ApacheHttpclientV3Test {
Privacy privacy = new TestPrivacy(privateKey, certificateProvider);
Signature signature = new DefaultSignature(certificateProvider,
new TestSigner(privateKey, "6CDDAA92CAD75998325027647847330C1756291"),
new TestVerifier(certificateProvider));
new TestVerifier(certificateProvider),
new DefaultSequencer());
return new TestProfile(
privacy, signature, new DefaultChannel.Builder()

View File

@ -45,7 +45,8 @@ class ApacheHttpclientValidTest {
Privacy privacy = new TestPrivacy(privateKey, certificateProvider);
Signature signature = new DefaultSignature(certificateProvider,
new TestSigner(privateKey, "823CF3E310F2E2ED1AF85506E74A95DC4301006FDEF2FD019953FAF4DE12A8BF"),
new TestVerifier(certificateProvider));
new TestVerifier(certificateProvider),
new DefaultSequencer());
return new TestProfile(
privacy, signature, new DefaultChannel.Builder()

View File

@ -31,6 +31,7 @@ public class RsaProfile extends AbstractProfile {
private Signature signature;
private Channel channel;
private HttpProfile httpProfile;
private Sequencer sequencer;
public Builder privateKey(PrivateKey privateKey) {
this.privateKey = privateKey;
@ -65,15 +66,25 @@ public class RsaProfile extends AbstractProfile {
return this;
}
public Builder sequencer(Sequencer sequencer) {
this.sequencer = sequencer;
return this;
}
public RsaProfile build() {
requireNonNull(this.channel);
if (Objects.isNull(this.sequencer)) {
this.sequencer = new DefaultSequencer();
}
CertificateProvider certificateProvider = new LocalCertificateProvider(certificates);
this.privacy = new RsaPrivacy(privateKey, certificateProvider);
X509Certificate certificate = certificateProvider.getAvailableCertificate();
this.signature = new DefaultSignature(certificateProvider,
new RsaSigner(privateKey, certificate.getSerialNumber().toString(HEX)),
new RsaVerifier(certificateProvider));
new RsaVerifier(certificateProvider),
this.sequencer);
httpProfile(httpProfile);
if (Objects.isNull(httpProfile)) {

View File

@ -45,7 +45,10 @@ public class MockResponse {
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));
Signature signature = new DefaultSignature(certificateProvider,
new TestSigner(privateKey, "6CDDAA92CAD75998325027647847330C1756291"),
new TestVerifier(certificateProvider),
new DefaultSequencer());
return new TestProfile(
privacy, signature, new DefaultChannel.Builder()

View File

@ -41,6 +41,7 @@ public final class SmProfile extends AbstractProfile {
private Signature signature;
private Channel channel;
private HttpProfile httpProfile;
private Sequencer sequencer;
public Builder privateKey(PrivateKey privateKey) {
this.privateKey = privateKey;
@ -81,9 +82,18 @@ public final class SmProfile extends AbstractProfile {
return this;
}
public Builder sequencer(Sequencer sequencer) {
this.sequencer = sequencer;
return this;
}
public SmProfile build() {
Objects.requireNonNull(this.channel);
if (Objects.isNull(this.sequencer)) {
this.sequencer = new DefaultSequencer();
}
// 证书加载器
CertificateProvider certificateProvider = new LocalCertificateProvider(certificates);
// 加密器
@ -91,7 +101,8 @@ public final class SmProfile extends AbstractProfile {
// 签名器
this.signature = new DefaultSignature(certificateProvider,
new Sm2Signer(privateKey, certificateSerial),
new Sm2Verifier(certificateProvider));
new Sm2Verifier(certificateProvider),
this.sequencer);
httpProfile(httpProfile);
if (Objects.isNull(httpProfile)) {

View File

@ -2,6 +2,8 @@ package com.czcb.scfs.spring.boot.starter;
import com.czcb.scfs.api.core.ApiClient;
import com.czcb.scfs.api.core.Profile;
import com.czcb.scfs.api.core.cipher.DefaultSequencer;
import com.czcb.scfs.api.core.cipher.Sequencer;
import com.czcb.scfs.api.core.http.ApiClientBuilder;
import com.czcb.scfs.api.service.echo.EchoService;
import com.czcb.scfs.api.service.v2.account.AccountService;
@ -18,7 +20,7 @@ import com.czcb.scfs.api.service.v2.ocr.OcrService;
import com.czcb.scfs.api.service.v2.pay.PayService;
import com.czcb.scfs.api.service.v2.sms.SmsService;
import com.czcb.scfs.api.service.v2.trans.TransService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
@ -49,16 +51,25 @@ public class ScfsAutoConfiguration {
*
*/
@Bean
@ConditionalOnClass(EchoService.class)
@ConditionalOnMissingBean
public EchoService echoService(ApiClient apiClient) {
return new EchoService(apiClient);
}
/**
*
*/
@Bean
@ConditionalOnMissingBean
public Sequencer sequencer() {
return new DefaultSequencer();
}
/**
*
*/
@Bean
@ConditionalOnClass(AccountService.class)
@ConditionalOnMissingBean
public AccountService accountService(ApiClient apiClient) {
return new AccountService(apiClient);
}
@ -67,7 +78,7 @@ public class ScfsAutoConfiguration {
*
*/
@Bean
@ConditionalOnClass(BillService.class)
@ConditionalOnMissingBean
public BillService billService(ApiClient apiClient) {
return new BillService(apiClient);
}
@ -76,7 +87,7 @@ public class ScfsAutoConfiguration {
*
*/
@Bean
@ConditionalOnClass(BmdService.class)
@ConditionalOnMissingBean
public BmdService bmdService(ApiClient apiClient) {
return new BmdService(apiClient);
}
@ -85,7 +96,7 @@ public class ScfsAutoConfiguration {
*
*/
@Bean
@ConditionalOnClass(CommunalDataService.class)
@ConditionalOnMissingBean
public CommunalDataService communalDataService(ApiClient apiClient) {
return new CommunalDataService(apiClient);
}
@ -95,7 +106,7 @@ public class ScfsAutoConfiguration {
*
*/
@Bean
@ConditionalOnClass(FaceService.class)
@ConditionalOnMissingBean
public FaceService faceService(ApiClient apiClient) {
return new FaceService(apiClient);
}
@ -104,7 +115,7 @@ public class ScfsAutoConfiguration {
*
*/
@Bean
@ConditionalOnClass(FileService.class)
@ConditionalOnMissingBean
public FileService fileService(ApiClient apiClient) {
return new FileService(apiClient);
}
@ -113,7 +124,7 @@ public class ScfsAutoConfiguration {
*
*/
@Bean
@ConditionalOnClass(LoanService.class)
@ConditionalOnMissingBean
public LoanService loanService(ApiClient apiClient) {
return new LoanService(apiClient);
}
@ -122,7 +133,7 @@ public class ScfsAutoConfiguration {
* OCR
*/
@Bean
@ConditionalOnClass(OcrService.class)
@ConditionalOnMissingBean
public OcrService ocrService(ApiClient apiClient) {
return new OcrService(apiClient);
}
@ -131,7 +142,7 @@ public class ScfsAutoConfiguration {
*
*/
@Bean
@ConditionalOnClass(PayService.class)
@ConditionalOnMissingBean
public PayService payService(ApiClient apiClient) {
return new PayService(apiClient);
}
@ -140,7 +151,7 @@ public class ScfsAutoConfiguration {
*
*/
@Bean
@ConditionalOnClass(SmsService.class)
@ConditionalOnMissingBean
public SmsService smsService(ApiClient apiClient) {
return new SmsService(apiClient);
}
@ -149,7 +160,7 @@ public class ScfsAutoConfiguration {
*
*/
@Bean
@ConditionalOnClass(TransService.class)
@ConditionalOnMissingBean
public TransService transService(ApiClient apiClient) {
return new TransService(apiClient);
}
@ -158,19 +169,19 @@ public class ScfsAutoConfiguration {
*
*/
@Bean
@ConditionalOnClass(InvoiceService.class)
@ConditionalOnMissingBean
public InvoiceService invoiceService(ApiClient apiClient) {
return new InvoiceService(apiClient);
}
@Bean
@ConditionalOnClass(ProjectService.class)
@ConditionalOnMissingBean
public ProjectService projectService(ApiClient apiClient) {
return new ProjectService(apiClient);
}
@Bean
@ConditionalOnClass(OrderService.class)
@ConditionalOnMissingBean
public OrderService orderService(ApiClient apiClient) {
return new OrderService(apiClient);
}

View File

@ -72,6 +72,11 @@ class ScfsAutoConfigurationTest {
Assertions.assertNotNull(configuration.echoService(apiClient()));
}
@Test
void sequencer() {
Assertions.assertNotNull(configuration.sequencer());
}
@Test
void accountService() {
Assertions.assertNotNull(configuration.accountService(apiClient()));
@ -126,4 +131,19 @@ class ScfsAutoConfigurationTest {
void transService() {
Assertions.assertNotNull(configuration.transService(apiClient()));
}
@Test
void invoiceService() {
Assertions.assertNotNull(configuration.invoiceService(apiClient()));
}
@Test
void projectService() {
Assertions.assertNotNull(configuration.projectService(apiClient()));
}
@Test
void orderService() {
Assertions.assertNotNull(configuration.orderService(apiClient()));
}
}

View File

@ -1,10 +1,7 @@
package com.czcb.scfs.api.test.service;
import com.czcb.scfs.api.service.v2.invoice.InvoiceService;
import com.czcb.scfs.api.service.v2.invoice.model.InvoiceDepositQueryRequest;
import com.czcb.scfs.api.service.v2.invoice.model.InvoiceDepositQueryResponse;
import com.czcb.scfs.api.service.v2.invoice.model.InvoiceInfoSaveRequest;
import com.czcb.scfs.api.service.v2.invoice.model.InvoiceSettleMatchRequest;
import com.czcb.scfs.api.service.v2.invoice.model.*;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@ -60,4 +57,17 @@ class InvoiceServiceTest {
InvoiceDepositQueryResponse response = invoiceService.invoiceDepositQuery(request);
System.out.println(response);
}
@Test
void invoiceSettleRepay() {
InvoiceSettleRepayRequest request = new InvoiceSettleRepayRequest();
request.setChannelNo("1039");
request.setAppNo("21");
request.setSerialNo("123456");
request.setTransDate("2024-07-24");
request.setTransTradeTime("2024-07-24 15:52:11");
InvoiceSettleRepayResponse response = invoiceService.invoiceSettleRepay(request);
System.out.println(response);
}
}

View File

@ -1,8 +1,19 @@
package com.czcb.scfs.api.test.service;
import com.czcb.scfs.api.service.v2.loan.LoanService;
import com.czcb.scfs.api.service.v2.loan.model.ApplyPersonRequest;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
@SpringBootTest
class LoanServiceTest {
@Resource
private LoanService loanService;
@Test
void applyPerson() {
loanService.applyPerson(new ApplyPersonRequest());
}
}