From bb26c2aec815367d32cd474a776cfe59aa2ef6c9 Mon Sep 17 00:00:00 2001 From: zhaoyiran Date: Wed, 17 Apr 2024 17:06:09 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 188 +++++---- src/main/java/com/win/bank/Application.java | 21 +- .../com/win/bank/config/MyBatisConfig.java | 28 ++ .../com/win/bank/dal/dataobject/BaseDO.java | 5 + .../win/bank/dal/dataobject/bank/BankDO.java | 77 ++++ .../win/bank/dal/mysql/bank/BankMapper.java | 9 + .../com/win/bank/service/MainService.java | 29 ++ .../win/bank/service/bank/BankService.java | 10 + .../bank/service/bank/BankServiceImpl.java | 21 + .../com/win/bank/service/cmb/ApiDemo.java | 73 ++++ .../win/bank/service/cmb/CmbServiceImpl.java | 31 ++ .../com/win/bank/service/cmb/DcHelper.java | 369 ++++++++++++++++++ .../com/win/bank/service/cmb/DeviceInfo.java | 39 ++ .../com/win/bank/service/demo/Student.java | 56 +++ .../java/com/win/bank/service/demo/Test.java | 48 +++ .../win/bank/service/demo/XStreamUtil.java | 20 + src/main/resources/application-dev.yml | 27 +- src/main/resources/application.yml | 10 +- src/main/resources/mapper/bank/BankMapper.xml | 12 + 19 files changed, 955 insertions(+), 118 deletions(-) create mode 100644 src/main/java/com/win/bank/config/MyBatisConfig.java create mode 100644 src/main/java/com/win/bank/dal/dataobject/BaseDO.java create mode 100644 src/main/java/com/win/bank/dal/dataobject/bank/BankDO.java create mode 100644 src/main/java/com/win/bank/dal/mysql/bank/BankMapper.java create mode 100644 src/main/java/com/win/bank/service/MainService.java create mode 100644 src/main/java/com/win/bank/service/bank/BankService.java create mode 100644 src/main/java/com/win/bank/service/bank/BankServiceImpl.java create mode 100644 src/main/java/com/win/bank/service/cmb/ApiDemo.java create mode 100644 src/main/java/com/win/bank/service/cmb/CmbServiceImpl.java create mode 100644 src/main/java/com/win/bank/service/cmb/DcHelper.java create mode 100644 src/main/java/com/win/bank/service/cmb/DeviceInfo.java create mode 100644 src/main/java/com/win/bank/service/demo/Student.java create mode 100644 src/main/java/com/win/bank/service/demo/Test.java create mode 100644 src/main/java/com/win/bank/service/demo/XStreamUtil.java create mode 100644 src/main/resources/mapper/bank/BankMapper.xml diff --git a/pom.xml b/pom.xml index b4ae482..e25b444 100644 --- a/pom.xml +++ b/pom.xml @@ -1,100 +1,118 @@ - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 2.7.15 - - - com.win - qad - 1.0.0 - mq - win mq - - 17 - - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.7.15 + + + com.win + bank + 1.0.0 + bank + win bank + + 17 + + + + org.bouncycastle + bcprov-jdk15on + 1.61 + + + com.google.code.gson + gson + 2.8.6 + + + dom4j + dom4j + 1.1 + + + com.jcraft + jsch + 0.1.55 + - - com.jcraft - jsch - 0.1.55 - + + org.apache.commons + commons-lang3 + 3.14.0 + - - org.apache.commons - commons-lang3 - 3.9 - + + mysql + mysql-connector-java + 8.0.33 + - - org.apache.commons - commons-exec - 1.3 - + + com.alibaba + fastjson + 1.2.83 + - - com.alibaba - fastjson - 1.2.83 - + + org.projectlombok + lombok + 1.18.28 + - - org.projectlombok - lombok - 1.18.28 - + + com.alibaba + druid-spring-boot-starter + 1.2.19 + - - com.alibaba - druid-spring-boot-starter - 1.2.19 - + + com.baomidou + mybatis-plus-boot-starter + 3.5.3.2 + - - com.baomidou - mybatis-plus-boot-starter - 3.5.3.2 - + + org.springframework.boot + spring-boot-starter + - - org.springframework.boot - spring-boot-starter - + + org.springframework.boot + spring-boot-starter-test + test + - - org.springframework.boot - spring-boot-starter-test - test - + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-jdbc + + - - org.springframework.boot - spring-boot-starter-web - - - - - - ${project.artifactId} - - - - org.springframework.boot - spring-boot-maven-plugin - - - - repackage - - - - - - + + + ${project.artifactId} + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + diff --git a/src/main/java/com/win/bank/Application.java b/src/main/java/com/win/bank/Application.java index 3d5dce2..1a00085 100644 --- a/src/main/java/com/win/bank/Application.java +++ b/src/main/java/com/win/bank/Application.java @@ -1,18 +1,27 @@ package com.win.bank; +import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.scheduling.annotation.EnableScheduling; /** * 启动程序 * * @author win */ -@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) +// @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) +@SuppressWarnings("SpringComponentScan") // 忽略 IDEA 无法识别 ${win.info.base-package} +// @SpringBootApplication(scanBasePackages = {"${win.info.base-package}.server", "${win.info.base-package}.module"}, +// exclude = {DataSourceAutoConfiguration.class, DruidDataSourceAutoConfigure.class}) +@SpringBootApplication(scanBasePackages = {"${win.info.base-package}.server", "${win.info.base-package}.module"}) +@EnableScheduling +@ComponentScan("com.win") +@MapperScan("com.win.bank.dal.mysql") public class Application { - public static void main(String[] args) { - SpringApplication.run(Application.class, args); - System.out.println("闻音启动成功"); - } + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + System.out.println("闻音启动成功"); + } } diff --git a/src/main/java/com/win/bank/config/MyBatisConfig.java b/src/main/java/com/win/bank/config/MyBatisConfig.java new file mode 100644 index 0000000..ab76912 --- /dev/null +++ b/src/main/java/com/win/bank/config/MyBatisConfig.java @@ -0,0 +1,28 @@ +package com.win.bank.config; + +import javax.sql.DataSource; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionFactoryBean; +import org.mybatis.spring.SqlSessionTemplate; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@MapperScan(value = "com.win.bank", annotationClass = Mapper.class) // Mapper 懒加载,目前仅用于单元测试 +public class MyBatisConfig { + + @Bean + public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { + SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); + sqlSessionFactoryBean.setDataSource(dataSource); + return sqlSessionFactoryBean.getObject(); + } + + @Bean + public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { + return new SqlSessionTemplate(sqlSessionFactory); + } +} diff --git a/src/main/java/com/win/bank/dal/dataobject/BaseDO.java b/src/main/java/com/win/bank/dal/dataobject/BaseDO.java new file mode 100644 index 0000000..cb2c7bb --- /dev/null +++ b/src/main/java/com/win/bank/dal/dataobject/BaseDO.java @@ -0,0 +1,5 @@ +package com.win.bank.dal.dataobject; + +import java.io.Serializable; + +public abstract class BaseDO implements Serializable {} diff --git a/src/main/java/com/win/bank/dal/dataobject/bank/BankDO.java b/src/main/java/com/win/bank/dal/dataobject/bank/BankDO.java new file mode 100644 index 0000000..b3b2227 --- /dev/null +++ b/src/main/java/com/win/bank/dal/dataobject/bank/BankDO.java @@ -0,0 +1,77 @@ +package com.win.bank.dal.dataobject.bank; + +import java.math.BigDecimal; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.win.bank.dal.dataobject.BaseDO; + +import lombok.*; + +/** + * 银企直联 DO + * + * @author 超级管理员 + */ +@TableName("basic_bank") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BankDO extends BaseDO { + /** + * id + */ + @TableId + private Long id; + /** + * 状态,0:未处理;1:支付中;2:支付成功;3:支付失败 + */ + private String status; + /** + * 付款银行编码,例:CMB:招商银行;ICBC:工商银行;BC:中国银行 + */ + private String payBankCode; + /** + * 付款账号 + */ + private String payAccount; + /** + * 收款账号 + */ + private String rcvAccount; + /** + * 收款户名 + */ + private String rcvUserName; + /** + * 收款方开户行名称 + */ + private String rcvBankName; + /** + * 收款方开户行地址 + */ + private String rcvBankAddress; + /** + * 收款方联行号 + */ + private String rcvBankNumber; + /** + * 交易金额 + */ + private BigDecimal amount; + /** + * 用途 + */ + private String purpose; + /** + * 提示信息 + */ + private String message; + /** + * 流水号 + */ + private String serialNumber; +} diff --git a/src/main/java/com/win/bank/dal/mysql/bank/BankMapper.java b/src/main/java/com/win/bank/dal/mysql/bank/BankMapper.java new file mode 100644 index 0000000..99cccaa --- /dev/null +++ b/src/main/java/com/win/bank/dal/mysql/bank/BankMapper.java @@ -0,0 +1,9 @@ +package com.win.bank.dal.mysql.bank; + +import org.apache.ibatis.annotations.Mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.win.bank.dal.dataobject.bank.BankDO; + +@Mapper +public interface BankMapper extends BaseMapper {} diff --git a/src/main/java/com/win/bank/service/MainService.java b/src/main/java/com/win/bank/service/MainService.java new file mode 100644 index 0000000..81a701d --- /dev/null +++ b/src/main/java/com/win/bank/service/MainService.java @@ -0,0 +1,29 @@ +package com.win.bank.service; + +import java.util.List; + +import javax.annotation.Resource; + +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import com.win.bank.dal.dataobject.bank.BankDO; +import com.win.bank.service.bank.BankService; + +@Component +public class MainService { + + @Resource + private BankService bankService; + + @Scheduled(cron = "* * * * * ?") + public void task() { + System.out.println(System.currentTimeMillis()); + + List bankDOList = bankService.listByStatus("0"); + + for (BankDO bankDO : bankDOList) { + System.out.println(bankDO); + } + } +} diff --git a/src/main/java/com/win/bank/service/bank/BankService.java b/src/main/java/com/win/bank/service/bank/BankService.java new file mode 100644 index 0000000..803ff9e --- /dev/null +++ b/src/main/java/com/win/bank/service/bank/BankService.java @@ -0,0 +1,10 @@ +package com.win.bank.service.bank; + +import java.util.List; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.win.bank.dal.dataobject.bank.BankDO; + +public interface BankService extends IService { + List listByStatus(String status); +} diff --git a/src/main/java/com/win/bank/service/bank/BankServiceImpl.java b/src/main/java/com/win/bank/service/bank/BankServiceImpl.java new file mode 100644 index 0000000..aa1c83f --- /dev/null +++ b/src/main/java/com/win/bank/service/bank/BankServiceImpl.java @@ -0,0 +1,21 @@ +package com.win.bank.service.bank; + +import java.util.List; + +import org.springframework.stereotype.Service; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.win.bank.dal.dataobject.bank.BankDO; +import com.win.bank.dal.mysql.bank.BankMapper; + +@Service +public class BankServiceImpl extends ServiceImpl implements BankService { + + @Override + public List listByStatus(String status) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("status", status); + return list(queryWrapper); + } +} diff --git a/src/main/java/com/win/bank/service/cmb/ApiDemo.java b/src/main/java/com/win/bank/service/cmb/ApiDemo.java new file mode 100644 index 0000000..508525f --- /dev/null +++ b/src/main/java/com/win/bank/service/cmb/ApiDemo.java @@ -0,0 +1,73 @@ +package com.win.bank.service.cmb; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.Security; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Random; + +import org.bouncycastle.crypto.CryptoException; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +/** + * 招商银行银企直联国密免前置/SaaS对接示例,本示例仅供参考,不保证各种异常场景运行,请勿直接使用,如有错漏请联系对接人员。运行时,请使用所获取的测试资源替换 用户编号、公私钥、对称密钥、服务商编号等信息。 + * + * @author cmb.firmbank + * @date 2023/7/20 + */ +public class ApiDemo { + + private static final int BOUND_START = 1000000; + private static final int BOUND_END = 9000000; + // 测试地址,生产需要替换 + private static String url = "http://192.168.0.142:8080/cdcserver/api/v2"; + // private static String url = "http://cdctest.cmburl.cn:80/cdcserver/api/v2"; + // 生产地址 + // private static String url = "https://cdc.cmbchina.com/cdcserver/api/v2"; + // 银行公钥,生产需要替换 + private static String publicKey = "BNsIe9U0x8IeSe4h/dxUzVEz9pie0hDSfMRINRXc7s1UIXfkExnYECF4QqJ2SnHxLv3z/99gsfDQrQ6dzN5lZj0="; + // 生产环境银行公钥 + // private static String publicKey = + // "BEynMEZOjNpwZIiD9jXtZSGr3Ecpwn7r+m+wtafXHb6VIZTnugfuxhcKASq3hX+KX9JlHODDl9/RDKQv4XLOFak="; + // 客户私钥,生产需要替换 + private static String privateKey = "NBtl7WnuUtA2v5FaebEkU0/Jj1IodLGT6lQqwkzmd2E="; + // 对称密钥,生产需要替换 + private static String symKey = "VuAzSWQhsoNqzn0K"; + + // 企业网银用户号,生产需要替换 + private static String uid = "N002986522"; + + private static Random random = new Random(); + + private ApiDemo() {} + + public static void main(String[] args) throws GeneralSecurityException, IOException, CryptoException { + // 装载BC库,必须在应用的启动类中调用此函数 + Security.addProvider(new BouncyCastleProvider()); + System.setProperty("sun.net.http.retryPost", "false"); + + // 业务接口名,这里是查询业务模式接口,生产请替换为对应接口名 + String funcode = "BB1PAYQR"; + // 准备接口数据,生产请替换为具体接口请求报文,包含所需的请求字段 + String currentDatetime = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); + String reqid = new SimpleDateFormat("yyyyMMddHHmmssSSSS").format(new Date()) + (BOUND_START + random.nextInt(BOUND_END)); + String data = + "{\"request\":{\"body\":{\"bb1paybmx1\":[{\"busCod\":\"N02030\",\"busMod\":\"00002\"}],\"bb1payopx1\":[{\"ccyNbr\":\"10\",\"crtAcc\":\"6214831150131511\",\"crtNam\":\"吴极客\",\"dbtAcc\":\"755936045010501\",\"nusAge\":\"用途\",\"bnkFlg\":\"Y\",\"trsAmt\":\"1.05\",\"yurRef\":\"2024041709400601\"}]},\"head\":{\"funcode\":\"BB1PAYOP\",\"userid\":\"N002986522\"}}}"; + + // String data = + // "{\"request\":{\"body\":{\"bb1payqrx1\":[{\"busCod\":\"N02030\",\"yurRef\":\"2024041709400602\"}]},\"head\":{\"funcode\":\"BB1PAYQR\",\"userid\":\"N002986522\"}}}"; + System.out.println(data); + DcHelper dchelper = new DcHelper(url, uid, privateKey, publicKey, symKey); + // 添加设备信息,请根据文档指引填写真实的信息,参数按顺序分别为:出口IP(无需)、MAC地址、CPU-ID、计算机名、主板ID、主板厂商 + // DeviceInfo deviceInfo = new DeviceInfo(null, "9822EFF12C6B", "AABBCCDDEEFFGGHH", "XXX", "XXX", "XXX"); + // String response = dchelper.sendRequest(data, funcode); + // process("Api请求1成功,响应报文:\r\n" + response); + // String response2 = dchelper.sendRequest(data, funcode, deviceInfo); + // process("Api请求2成功,响应报文:\r\n" + response2); + } + + private static void process(String response) { + + } +} diff --git a/src/main/java/com/win/bank/service/cmb/CmbServiceImpl.java b/src/main/java/com/win/bank/service/cmb/CmbServiceImpl.java new file mode 100644 index 0000000..c80222e --- /dev/null +++ b/src/main/java/com/win/bank/service/cmb/CmbServiceImpl.java @@ -0,0 +1,31 @@ +package com.win.bank.service.cmb; + +import org.springframework.stereotype.Service; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.win.bank.dal.dataobject.bank.BankDO; + +@Service +public class CmbServiceImpl { + + public String payment(BankDO bankDO) { + String data = "{\"request\":{\"body\":{\"bb1paybmx1\":[{\"busCod\":\"N02030\",\"busMod\":\"00002\"}]},\"head\":{\"funcode\":\"BB1PAYOP\",\"userid\":\"N002986522\"}}}"; + JsonObject requestJson = new Gson().fromJson(data, JsonObject.class); + JsonArray bb1payopx1 = new JsonArray(); + JsonObject info = new JsonObject(); + info.addProperty("dbtAcc", bankDO.getPayAccount()); + info.addProperty("crtAcc", bankDO.getRcvAccount()); + info.addProperty("crtNam", bankDO.getRcvUserName()); + info.addProperty("ccyNbr", "10"); + info.addProperty("trsAmt", bankDO.getAmount().toString()); + info.addProperty("nusAge", bankDO.getPurpose()); + info.addProperty("bnkFlg", "Y"); + info.addProperty("yurRef", bankDO.getId().toString()); + bb1payopx1.add(info); + requestJson.getAsJsonObject("request").getAsJsonObject("body").add("bb1payopx1", bb1payopx1); + System.out.println(requestJson); + return null; + } +} diff --git a/src/main/java/com/win/bank/service/cmb/DcHelper.java b/src/main/java/com/win/bank/service/cmb/DcHelper.java new file mode 100644 index 0000000..a0698ed --- /dev/null +++ b/src/main/java/com/win/bank/service/cmb/DcHelper.java @@ -0,0 +1,369 @@ +package com.win.bank.service.cmb; + +import java.io.*; +import java.math.BigInteger; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.util.*; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import javax.net.ssl.*; + +import org.bouncycastle.asn1.*; +import org.bouncycastle.crypto.CryptoException; +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.bouncycastle.crypto.params.ParametersWithID; +import org.bouncycastle.crypto.signers.SM2Signer; +import org.bouncycastle.jce.ECNamedCurveTable; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.spec.ECParameterSpec; +import org.bouncycastle.math.ec.ECPoint; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +/** + * 招商银行银企直联国密免前置/SaaS对接示例,本示例仅供参考,不保证各种异常场景运行,请勿直接使用,如有错漏请联系对接人员。运行时,请使用所获取的测试资源替换 用户编号、公私钥、对称密钥、服务商编号等信息。 + * + * @author cmb.firmbank + * @date 2023/7/20 + */ +public class DcHelper { + + private static final int LENGTH_32 = 32; + private static final int USERID_LEN = 16; + private static final int CONNECT_TIMEOUT = 15000; + private static final int READ_TIMEOUT = 60000; + private static final int STATUS_OK = 200; + private static Base64.Encoder encoder = Base64.getEncoder(); + private static Base64.Decoder decoder = Base64.getDecoder(); + + // 请求URL + private final String url; + // 企业网银用户号 + private final String uid; + // 国密算法向量,根据用户号生成 + private final byte[] userId; + // 算法,固定为国密算法 + private final String alg; + // 客户私钥 + private final byte[] privateKey; + // 银行公钥 + private final byte[] publicKey; + // 协商的对称密钥 + private final byte[] symKey; + + private static final Logger logger = LoggerFactory.getLogger(DcHelper.class); + + public DcHelper(String url, String uid, String privateKey, String publicKey, String symKey) { + this.url = url; + this.uid = uid; + this.userId = getUserId(uid); + this.alg = "SM"; + this.privateKey = decoder.decode(privateKey); + this.publicKey = decoder.decode(publicKey); + this.symKey = symKey.getBytes(StandardCharsets.UTF_8); + } + + public String sendRequest(String data, String funcode) throws IOException, CryptoException, GeneralSecurityException { + // 增加设备信息 + JsonObject requestJson = new Gson().fromJson(data, JsonObject.class); + // requestJson.getAsJsonObject("request").add("exthdr", deviceInfo.toJson()); + // 对请求报文做排序 + String source = recursiveKeySort(requestJson); + // 生成签名 + // byte[] signature = cmbSM2SignWithSM3(userId, privateKey, source.getBytes(StandardCharsets.UTF_8)); + // 替换签名字段 + // requestJson.getAsJsonObject("signature").addProperty("sigdat", new String(encoder.encode(signature), + // StandardCharsets.UTF_8)); + + // 对数据进行对称加密 + // String request = requestJson.toString(); + // byte[] encryptRequest = cmbSM4Crypt(symKey, userId, request.getBytes(StandardCharsets.UTF_8), 1); + // String encryptedRequest = new String(encoder.encode(encryptRequest), StandardCharsets.UTF_8); + logger.debug(data); + // 发送请求 + HashMap map = new HashMap<>(); + map.put("UID", uid); + map.put("ALG", alg); + map.put("DATA", URLEncoder.encode(data, StandardCharsets.UTF_8.displayName())); + // map.put("DATA", URLEncoder.encode(encryptedRequest, StandardCharsets.UTF_8.displayName())); + map.put("FUNCODE", funcode); + String response = httpPost(url, map); + // logger.debug(response); + printJson(data); + if (response.startsWith("CDCServer:")) { + throw new IOException("访问目标地址 " + url + " 失败:" + response); + } + + // 返回结果解密 + // response = new String((cmbSM4Crypt(symKey, userId, decoder.decode(response), 2)), StandardCharsets.UTF_8); + + // 验证签名是否正确 + // JsonObject responseJson = new Gson().fromJson(response, JsonObject.class); + // JsonObject signatureJson = responseJson.getAsJsonObject("signature"); + // String responseSignature = signatureJson.get("sigdat").getAsString(); + // signatureJson.addProperty("sigdat", "__signature_sigdat__"); + // responseJson.add("signature", signatureJson); + // String responseSorted = recursiveKeySort(responseJson); + // boolean verify = cmbSM2VerifyWithSM3(userId, publicKey, responseSorted.getBytes(StandardCharsets.UTF_8), + // decoder.decode(responseSignature)); + // if (!verify) { + // throw new IOException("响应报文的签名无效"); + // } + printJson(response); + return response; + } + + private static String recursiveKeySort(JsonObject json) { + StringBuilder appender = new StringBuilder(); + appender.append("{"); + Iterator keys = new TreeSet<>(json.keySet()).iterator(); + boolean isFirstEle = true; + while (keys.hasNext()) { + if (!isFirstEle) { + appender.append(","); + } + String key = keys.next(); + Object val = json.get(key); + if (val instanceof JsonObject) { + appender.append("\"").append(key).append("\":"); + appender.append(recursiveKeySort((JsonObject)val)); + } else if (val instanceof JsonArray) { + JsonArray jarray = (JsonArray)val; + appender.append("\"").append(key).append("\":["); + boolean isFirstArrEle = true; + for (int i = 0; i < jarray.size(); i++) { + if (!isFirstArrEle) { + appender.append(","); + } + Object obj = jarray.get(i); + if (obj instanceof JsonObject) { + appender.append(recursiveKeySort((JsonObject)obj)); + } else { + appender.append(obj.toString()); + } + isFirstArrEle = false; + } + appender.append("]"); + } else { + String value = val.toString(); + appender.append("\"").append(key).append("\":").append(value); + } + isFirstEle = false; + } + appender.append("}"); + return appender.toString(); + } + + private static byte[] getUserId(String uid) { + return (uid + "0000000000000000").substring(0, USERID_LEN).getBytes(); + } + + private static String httpPost(String httpUrl, Map param) throws IOException, GeneralSecurityException { + HttpURLConnection connection = null; + String result; + try { + URL url = new URL(httpUrl); + SSLContext sslcontext; + sslcontext = SSLContext.getInstance("SSL"); + TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init((KeyStore)null); + X509TrustManager defaultTm = null; + for (TrustManager tm : tmf.getTrustManagers()) { + if (tm instanceof X509TrustManager) { + defaultTm = (X509TrustManager)tm; + break; + } + } + sslcontext.init(null, new TrustManager[] {defaultTm}, new java.security.SecureRandom()); + HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext.getSocketFactory()); + + connection = (HttpURLConnection)url.openConnection(); + connection.setRequestMethod("POST"); + connection.setConnectTimeout(CONNECT_TIMEOUT); + connection.setReadTimeout(READ_TIMEOUT); + connection.setInstanceFollowRedirects(true); + + connection.setDoOutput(true); + connection.setDoInput(true); + + connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + try (OutputStream os = connection.getOutputStream()) { + os.write(createLinkString(param).getBytes()); + if (connection.getResponseCode() != STATUS_OK) { + InputStream is = connection.getErrorStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); + StringBuilder sbf = new StringBuilder(); + String temp; + while ((temp = br.readLine()) != null) { + sbf.append(temp); + sbf.append("\r\n"); + } + + result = sbf.toString(); + br.close(); + is.close(); + } else { + InputStream is = connection.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); + StringBuilder sbf = new StringBuilder(); + String temp; + while ((temp = br.readLine()) != null) { + sbf.append(temp); + } + result = sbf.toString(); + br.close(); + is.close(); + } + } + } finally { + if (connection != null) { + connection.disconnect(); + } + } + return result; + } + + private static String createLinkString(Map params) { + ArrayList keys = new ArrayList<>(params.keySet()); + Collections.sort(keys); + + StringBuilder prestr = new StringBuilder(); + for (int i = 0; i < keys.size(); i++) { + String key = keys.get(i); + String value = params.get(key); + if (i == keys.size() - 1) { + prestr.append(key).append("=").append(value); + } else { + prestr.append(key).append("=").append(value).append("&"); + } + } + return prestr.toString(); + } + + // 以下是加解密相关的函数 + + private static byte[] cmbSM2SignWithSM3(byte[] id, byte[] privkey, byte[] msg) throws IOException, CryptoException { + if (privkey == null || msg == null) { + throw new CryptoException("CMBSM2SignWithSM3 input error"); + } + ECPrivateKeyParameters privateKey = encodePrivateKey(privkey); + SM2Signer signer = new SM2Signer(); + ParametersWithID parameters = new ParametersWithID(privateKey, id); + signer.init(true, parameters); + signer.update(msg, 0, msg.length); + return decodeDERSignature(signer.generateSignature()); + } + + private static ECPrivateKeyParameters encodePrivateKey(byte[] value) { + BigInteger d = new BigInteger(1, value); + ECParameterSpec spec = ECNamedCurveTable.getParameterSpec("sm2p256v1"); + ECDomainParameters ecParameters = new ECDomainParameters(spec.getCurve(), spec.getG(), spec.getN(), spec.getH(), spec.getSeed()); + return new ECPrivateKeyParameters(d, ecParameters); + } + + private static byte[] decodeDERSignature(byte[] signature) throws IOException { + ASN1InputStream stream = new ASN1InputStream(new ByteArrayInputStream(signature)); + ASN1Sequence primitive = (ASN1Sequence)stream.readObject(); + Enumeration enumeration = primitive.getObjects(); + BigInteger intR = enumeration.nextElement().getValue(); + BigInteger intS = enumeration.nextElement().getValue(); + byte[] bytes = new byte[LENGTH_32 * 2]; + byte[] r = format(intR.toByteArray()); + byte[] s = format(intS.toByteArray()); + System.arraycopy(r, 0, bytes, 0, LENGTH_32); + System.arraycopy(s, 0, bytes, LENGTH_32, LENGTH_32); + return bytes; + } + + private static byte[] format(byte[] value) { + if (value.length == LENGTH_32) { + return value; + } else { + byte[] bytes = new byte[LENGTH_32]; + if (value.length > LENGTH_32) { + System.arraycopy(value, value.length - LENGTH_32, bytes, 0, LENGTH_32); + } else { + System.arraycopy(value, 0, bytes, LENGTH_32 - value.length, value.length); + } + return bytes; + } + } + + private static byte[] cmbSM4Crypt(byte[] key, byte[] iv, byte[] input, int mode) throws GeneralSecurityException { + SecretKeySpec spec = new SecretKeySpec(key, "SM4"); + IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); + Cipher cipher = Cipher.getInstance("SM4/CBC/PKCS7Padding", BouncyCastleProvider.PROVIDER_NAME); + cipher.init(mode, spec, ivParameterSpec); + return cipher.doFinal(input); + } + + private static boolean cmbSM2VerifyWithSM3(byte[] id, byte[] pubkey, byte[] msg, byte[] signature) throws IOException { + + if (pubkey == null || msg == null || signature == null) { + throw new IllegalArgumentException("CMBSM2VerifyWithSM3 input error"); + } + ECPublicKeyParameters publicKey = encodePublicKey(pubkey); + SM2Signer signer = new SM2Signer(); + ParametersWithID parameters = new ParametersWithID(publicKey, id); + signer.init(false, parameters); + signer.update(msg, 0, msg.length); + return signer.verifySignature(encodeDERSignature(signature)); + } + + private static ECPublicKeyParameters encodePublicKey(byte[] value) { + byte[] x = new byte[LENGTH_32]; + byte[] y = new byte[LENGTH_32]; + System.arraycopy(value, 1, x, 0, LENGTH_32); + System.arraycopy(value, LENGTH_32 + 1, y, 0, LENGTH_32); + BigInteger intX = new BigInteger(1, x); + BigInteger intY = new BigInteger(1, y); + ECParameterSpec spec = ECNamedCurveTable.getParameterSpec("sm2p256v1"); + ECPoint intQ = spec.getCurve().createPoint(intX, intY); + ECDomainParameters ecParameters = new ECDomainParameters(spec.getCurve(), spec.getG(), spec.getN(), spec.getH(), spec.getSeed()); + return new ECPublicKeyParameters(intQ, ecParameters); + } + + private static byte[] encodeDERSignature(byte[] signature) throws IOException { + byte[] r = new byte[LENGTH_32]; + byte[] s = new byte[LENGTH_32]; + System.arraycopy(signature, 0, r, 0, LENGTH_32); + System.arraycopy(signature, LENGTH_32, s, 0, LENGTH_32); + ASN1EncodableVector vector = new ASN1EncodableVector(); + vector.add(new ASN1Integer(new BigInteger(1, r))); + vector.add(new ASN1Integer(new BigInteger(1, s))); + return (new DERSequence(vector)).getEncoded(); + } + + private void printJson(String jsonStr) { + ObjectMapper mapper = new ObjectMapper(); + mapper.enable(SerializationFeature.INDENT_OUTPUT); + + try { + // 原始的JSON字符串 + // String jsonStr = "{\"name\": \"Alice\", \"age\": 30, \"city\": \"Beijing\"}"; + + // 格式化打印JSON字符串 + Object json = mapper.readValue(jsonStr, Object.class); + String formattedJsonStr = mapper.writeValueAsString(json); + // System.out.println(formattedJsonStr); + logger.debug(formattedJsonStr); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/com/win/bank/service/cmb/DeviceInfo.java b/src/main/java/com/win/bank/service/cmb/DeviceInfo.java new file mode 100644 index 0000000..fc25a66 --- /dev/null +++ b/src/main/java/com/win/bank/service/cmb/DeviceInfo.java @@ -0,0 +1,39 @@ +package com.win.bank.service.cmb; + +import com.google.gson.JsonObject; + +/** + * 设备信息 + */ +public class DeviceInfo { + + private String ipVal; + private String macVal; + private String cpuInfo; + private String computerName; + private String mainBoardId; + private String mainBoardName; + + public DeviceInfo(String ipVal, String macVal, String cpuInfo, String computerName, String mainBoardId, String mainBoardName) { + this.ipVal = ipVal; + this.macVal = macVal; + this.cpuInfo = cpuInfo; + this.computerName = computerName; + this.mainBoardId = mainBoardId; + this.mainBoardName = mainBoardName; + } + + public JsonObject toJson() { + JsonObject deviceInfo = new JsonObject(); + if (ipVal != null) { + deviceInfo.addProperty("ipType", ipVal.indexOf(':') != -1 ? "6" : "4"); + deviceInfo.addProperty("ipVal", ipVal); + } + deviceInfo.addProperty("macVal", macVal); + deviceInfo.addProperty("cpuInfo", cpuInfo); + deviceInfo.addProperty("cmpName", computerName); + deviceInfo.addProperty("mbId", mainBoardId); + deviceInfo.addProperty("mbMf", mainBoardName); + return deviceInfo; + } +} diff --git a/src/main/java/com/win/bank/service/demo/Student.java b/src/main/java/com/win/bank/service/demo/Student.java new file mode 100644 index 0000000..d7650ca --- /dev/null +++ b/src/main/java/com/win/bank/service/demo/Student.java @@ -0,0 +1,56 @@ +package com.win.bank.service.demo; + + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +public class Student { + + private String name; // 姓名 + private String sex; // 性别 + private int number; // 学号 + private String className; // 班级 + + public Student(){} + + public Student(String string, String string2, int i, String string3) { + this.name = string; + this.sex = string2; + this.className = string3; + + } + + @XmlElement(name = "name") + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + + @XmlElement(name = "sex") + public String getSex() { + return sex; + } + public void setSex(String sex) { + this.sex = sex; + } + + @XmlElement(name = "number") + public int getNumber() { + return number; + } + public void setNumber(int number) { + this.number = number; + } + + @XmlElement(name = "className") + public String getClassName() { + return className; + } + public void setClassName(String className) { + this.className = className; + } + +} \ No newline at end of file diff --git a/src/main/java/com/win/bank/service/demo/Test.java b/src/main/java/com/win/bank/service/demo/Test.java new file mode 100644 index 0000000..3be63fc --- /dev/null +++ b/src/main/java/com/win/bank/service/demo/Test.java @@ -0,0 +1,48 @@ +package com.win.bank.service.demo; + +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; + +import org.dom4j.Document; +import org.dom4j.io.OutputFormat; +import org.dom4j.io.SAXReader; +import org.dom4j.io.XMLWriter; + +/** + * @author admin + */ +public class Test { + public static void main(String[] args) throws Exception { + Student st = new Student("张三", "男", 10001, "尖"); + String xml = XStreamUtil.marshal(st, Student.class); + System.out.println(xml); + // System.out.println(formatXML(xml)); + } + + public static String formatXML(String inputXML) throws Exception { + SAXReader reader = new SAXReader(); + Document document = reader.read(new StringReader(inputXML)); + String requestXML = null; + XMLWriter writer = null; + if (document != null) { + try { + StringWriter stringWriter = new StringWriter(); + OutputFormat format = new OutputFormat(" ", true); + writer = new XMLWriter(stringWriter, format); + writer.write(document); + writer.flush(); + requestXML = stringWriter.getBuffer().toString(); + } finally { + if (writer != null) { + try { + writer.close(); + } catch (IOException e) { + } + } + } + } + return requestXML; + } + +} diff --git a/src/main/java/com/win/bank/service/demo/XStreamUtil.java b/src/main/java/com/win/bank/service/demo/XStreamUtil.java new file mode 100644 index 0000000..a81fd87 --- /dev/null +++ b/src/main/java/com/win/bank/service/demo/XStreamUtil.java @@ -0,0 +1,20 @@ +package com.win.bank.service.demo; + +import java.io.StringWriter; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; + +public class XStreamUtil { + public static String marshal(Object object, Class clazz) throws JAXBException { + JAXBContext jaxbContext = JAXBContext.newInstance(clazz); + Marshaller marshaller = jaxbContext.createMarshaller(); + marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + StringWriter writer = new StringWriter(); + marshaller.marshal(object, writer); + return writer.toString(); + } + +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index c2fbfa8..f336f5b 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -17,13 +17,10 @@ server: min-spare: 10 spring: - autoconfigure: - exclude: - - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置,使用 dynamic-datasource-spring-boot-starter 配置多数据源 datasource: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://dev.ccwin-in.com:23113/qad_dev?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + url: jdbc:mysql://dev.ccwin-in.com:23113/sfms_hella?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: learun password: Microdoft@2021 @@ -34,25 +31,3 @@ logging: level: com.win: debug org.springframework: warn - -shell: - jlht: - qad-domain: JLHT - company-code: CCWININ - server-id: master - ip: 222.169.228.163 - port: 6122 - linux-id: mfg - linux-password: mfgpro - qad-user: mfg - qad-password: Qadwin1 - jlht2: - qad-domain: JLHT2 - company-code: CCWININ - server-id: master - ip: 222.169.228.163 - port: 6122 - linux-id: mfg - linux-password: mfgpro - qad-user: mfg - qad-password: Qadwin1 diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 6c3ef23..80b7eda 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -25,9 +25,17 @@ mybatis-plus: logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) banner: false # 关闭控制台的 Banner 打印 - type-aliases-package: com.win.dao + type-aliases-package: com.win.bank.dal.dataobject + mapper-locations: classpath:/mapper/bank/*.xml + # type-aliases-package: ${win.info.base-package}.module.*.dal.dataobject encryptor: password: XDV71a+xqStEA3WH # 加解密的秘钥,可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成 mybatis-plus-join: banner: false # 关闭控制台的 Banner 打印 + +win: + info: + base-package: com.win + codegen: + base-package: ${win.info.base-package} \ No newline at end of file diff --git a/src/main/resources/mapper/bank/BankMapper.xml b/src/main/resources/mapper/bank/BankMapper.xml new file mode 100644 index 0000000..11861aa --- /dev/null +++ b/src/main/resources/mapper/bank/BankMapper.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file