• 调用CFCA金信反欺诈服务相关接口,很详细


    一、准备

    之前对接过CFCA安心签相关的接口,以为这次对接也会很麻烦,现实是这次比想象中的要简单一点,起码加白名单就很快。

    老规矩,先找CFCA对接的技术人员要相关资料,并让他们帮你的服务器加上白名单。

    这是金信反欺诈服务的产品,放在Excel表中,看需要使用哪一个产品

    在这里插入图片描述

    找到目标产品后,找对接的技术人员要相关的文档和demo。

    二、调用接口

    我这次对接的是运营商风险识别 (三要素详版),也就是三要素核验接口,以下称为三要素核验

    在这里插入图片描述

    1、查询接口文档

    这是三要素核验对应的技术文档,是一个PDF文件

    在这里插入图片描述

    先看请求参数

    在这里插入图片描述

    在这里插入图片描述

    对应请求体示例:

    {
    	"transcode": "209",
    	"sign": "...",
    	"version": "0100",
    	"ordersn": "...",
    	"dsorderid": "...",
    	"merchno": "...",
    	"timestamp": "1643170610550",
    	"data": {
    		"username": "用户名",
    		"idcard": "身份证",
    		"mobile": "手机号",
    		"idtype": "01",
    		"sceneCode": "0x",
    		"sCustomerName": "xx-xx-xx",
    		"scUsePurpose": "xxxxx",
    		"protocolVerNm": "xxxxxxx",
    		"serialNm": "xxxxxxxxx",
    		"reqIp": "",
    		"remark": ""
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    然后是响应参数

    在这里插入图片描述

    每个接口成功的响应码相同,失败的响应可能不一样,这是三要素核验的响应码

    在这里插入图片描述
    在这里插入图片描述

    2、查看代码示例

    三要素核验的代码示例在一个压缩包里,解压之后就可以找到对应的代码示例,所有的代码示例都在这里

    在这里插入图片描述

    解压之后可以看到所有的示例

    在这里插入图片描述

    不知道代码示例在那可以先看这里

    在这里插入图片描述

    基本上列举了所有产品接口

    在这里插入图片描述

    这是三要素核验的接口码

    在这里插入图片描述

    我要找的三要素核验示例代码在这

    在这里插入图片描述

    在这里插入图片描述

    对应三要素核验的示例代码

    package com.jinxin;
    
    import com.jinxin.utils.BaseTest;
    import com.jinxin.utils.TransCodeEnum;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.UUID;
    
    /**
     * author: weidong
     * create: 2021-06-13
     **/
    public class Test209 extends BaseTest {
    
        public static void main(String[] args) throws Exception {
    
            // 获取枚举类中指定业务的地址后缀部分,前缀需要在【商户信息.properties】填写
            String suffix = TransCodeEnum.TRANS_CODE_209.getUrl();
    
            // 请求最外层参数---------------------------------------------------------------------------------------
            Map<String, Object> reqMap = new HashMap<>();
            reqMap.put("transcode", TransCodeEnum.TRANS_CODE_209.getTranscode()); // 接口业务编码,具体业务请参考接口文档
            reqMap.put("merchno", BaseTest.merchNo); // 商户号
            reqMap.put("ordersn", UUID.randomUUID().toString().replaceAll("-", ""));    //商户流水号
            reqMap.put("dsorderid", UUID.randomUUID().toString().replaceAll("-", ""));  //商户订单号
            reqMap.put("version", "0100"); //api版本号
            reqMap.put("timestamp", System.currentTimeMillis() + ""); // 时间戳
    
            // 请求内层 data 参数====================================================================================
            Map<String, Object> data = new HashMap<>();
            data.put("username", "请输入姓名");
            data.put("mobile", "请输入手机号");
            data.put("idcard", "请输入身份证");
            data.put("idtype", "01");
    
            data.put("sCustomerName", ""); // 二级商户名称,例如:xxx商户名-yyy产品名称-zzz使用方法
            data.put("sceneCode", "");   //真实场景 见接口文档枚举
            data.put("scUsePurpose", ""); // 预留字段
            data.put("protocolVerNm", ""); // 预留字段
            data.put("serialNm", ""); // 预留字段
            data.put("reqIp", "");  // 请求ip
            data.put("remark", ""); // 备注
    
            request(reqMap, data, suffix); // 调用此方法设置拼接请求地址、生成签名信息、对数据进行加密,后发起请求、解密响应数据。
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    示例里面只有请求参数,没有响应参数,需要查看接口文档

    在这里插入图片描述

    调用接口还需要对应的证书文件,在这里

    在这里插入图片描述

    “jks”文件夹下来的所有文件都需要。

    3、测试调用接口

    调用接口不需要导入jar包,直接将demo中的代码拿过来并且正确传入参数,那应该就可以调用成功了,但调用的过程中感觉demo中的代码繁琐了点,就做了一些简化,代码如下:

    public static void main(String[] args) {
        CFCAJINXINConfig config = new CFCAJINXINConfig();
    
        config.setHost("请求地址前缀部分");
        config.setKeystorePath("客户通信证书路径(证书需要申请)");
        config.setKeystorePassword("客户通信证书密码");
        config.setTruststorePath("金信通信证书信任库路径(证书由金信提供)");
        config.setTruststorePassword("金信通信证书信任库密码");
        config.setMerchSM2PrivateKey("商户私钥");
        config.setJinXinSM2PublicKey("金信平台公钥");
        config.setMerchNo("商户号");
        config.setVersion("api版本号");
    
        config.setTranscode(CFCAJINXINCodeEnum.CODE_209.getCode());
    
        CFCAJINXINCode209ReqDto params = new CFCAJINXINCode209ReqDto();
        params.setUsername("你的名字");
        params.setMobile("你的手机号码");
        params.setIdcard("你的身份证号码");
        params.setIdtype("01");
    
        try {
            sendRequest(config, params);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    这里只是测试调用的代码,详情可以查看工具类那里,所有代码都放在那了。

    调用成功后控制台打印是这样的:

    在这里插入图片描述

    返回结果格式化后是这样的:

    在这里插入图片描述

    三、工具类

    1、CFCA金信反欺诈服务接口码枚举类

    /**
     * CFCA金信反欺诈服务接口码枚举类
     *
     * @author:gan
     * @date: 2023-09-21 09:41
     */
    public enum CFCAJINXINCodeEnum {
    
        CODE_105("105", "银行卡风险识别(二要素简版))", "/jxdata/api/auth/jm/execute2.do", ""),
        CODE_106("106", "银行卡风险识别(三要素简版))", "/jxdata/api/auth/jm/execute2.do", ""),
        CODE_107("107", "银行卡风险识别(四要素简版))", "/jxdata/api/auth/jm/execute2.do", ""),
        CODE_108("108", "运营商风险识别(二要素))", "/jxdata/api/auth/jm/execute2.do", ""),
    
        CODE_109("109", "运营商风险识别(三要素)", "/jxdata/api/auth/jm/execute2.do", ""),
        CODE_116("116", "银行卡风险识别(三要素详版)", "/jxdata/api/auth/jm/execute2.do", ""),
        CODE_117("117", "银行卡风险识别(四要素详版-非身份证)", "/jxdata/api/auth/jm/execute2.do", ""),
        CODE_118("118", "身份信息识别(国民二要素)", "/jxdata/api/auth/jm/execute2.do", ""),
        CODE_209("209", "运营商风险识别(三要素详版)", "/jxdata/api/auth/jm/execute2.do", ""),
        CODE_307("307", "身份信息识别(国民四要素)", "/jxdata/api/auth/jm/execute2.do", ""),
    
        // 313 业务有两个url,请查看 Test313代码中的路径
        CODE_313("313", "H5活体识别+人像比对", "", ""),
        // 314 业务有两个url,请查看 Test314代码中的路径
        CODE_314("314", "H5活体识别", "", ""),
    
        CODE_405("405", "银行卡账户风险协查", "/jxdata/api/auth/jm/execute2.do", ""),
        CODE_407("407", "账户单卡认证", "/jxdata/api/auth/jm/execute2.do", ""),
        CODE_408("408", "账户三要素认证", "/jxdata/api/auth/jm/execute2.do", ""),
        CODE_412("412", "银行卡账户风险协查(反欺诈识别)", "/jxdata/api/auth/jm/execute2.do", ""),
        CODE_610("610", "不良银联卡识别", "/jxdata/api/auth/jm/execute2.do", ""),
        CODE_611("611", "银联不良持卡人识别", "/jxdata/api/auth/jm/execute2.do", ""),
        CODE_612("612", "银联风险电话识别", "/jxdata/api/auth/jm/execute2.do", ""),
        CODE_901("901", "企业信息三要素认证", "/jxdata/api/auth/jm/execute2.do", ""),
        CODE_902("902", "对公账户二要素", "/jxdata/api/auth/jm/execute2.do", ""),
        CODE_906("906", "企业信息认证四要素", "/jxdata/api/auth/jm/execute2.do", ""),
    
        CODE_121("121", "身份信息识别(实名人像认证)", "/jxdata/api/auth/living/execute2.do", ""),
        CODE_124("124", "身份信息识别(身份证照片比对)", "/jxdata/api/auth/living/execute2.do", ""),
        CODE_311("311", "静默活体认证", "/jxdata/api/auth/living/execute2.do", ""),
    
        // 309 业务有两个url,请查看 Test309 代码中的路径
        CODE_309("309", "读数活体认证", "", ""),
    
        CODE_903("903", "企业工商信息查询", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_907("907", "LEI编码査企业信息", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_908("908", "企业四要素信息认证详版", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_910("910", "企业司法风险报告", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_911("911", "企业经营异常核查", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_912("912", "企业股权穿透识别", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_913("913", "企业经纬度查询", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_914("914", "税务登记号核验", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_915("915", "企业深度查验报告", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_916("916", "一址多照核查", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_917("917", "企业最终受益人查询", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_919("919", "纸质营业执照二维码查询", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_922("922", "严重违法核查", "/jxdata/api/auth/company/execute2.do", ""),
    
        // 923 业务有两个url,请查看 Test923 代码中的路径
        CODE_923_9231("923", "小额打款", "/jxdata/api/auth/company/execute2.do", ""),
    
        CODE_925("925", "发票要素核查", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_926("926", "企业工商详细信息查询", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_927("927", "诉讼信息核查", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_928("928", "企业知识产权核查", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_929("929", "税务信用列表核查", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_930("930", "进出口信用列表核查", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_9301("9301", "电子营业执照", "/jxdata/api/auth/company/execute2.do", ""),
        //    TRANS_CODE_903("926", "企业工商信息详细查询", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_932("932", "企业经营状况报告", "/jxdata/api/auth/company/execute2.do", ""),
    
    
        CODE_933("933", "证书状态查询", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_934("934", "证书所属机构查询", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_935("935", "企业证书信息验证", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_936("936", "企业多维智能反欺诈评分", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_937("937", "企业综合信用报告", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_938("938", "企业信用决策评分", "/jxdata/api/auth/company/execute2.do", ""),
    
        CODE_939("939", "个人严重违法核查", "/risk/api/execute2.do", ""),
    
        CODE_940("940", "电信诈骗识别(含账户涉诈/客户涉诈", "/risk/api/execute2.do", ""),
    
        CODE_941("941", "涉金融风险黑名单", "/risk/api/execute2.do", ""),
    
        CODE_942("942", "涉诉信息核查(详版)", "/risk/api/execute2.do", ""),
    
        CODE_945("945", "企业经营风险核查", "/risk/api/execute2.do", ""),
        CODE_946("946", "企业综合风险评分", "/risk/api/execute2.do", ""),
        CODE_947("947", "企业经营信息", "/risk/api/execute2.do", ""),
        CODE_948("948", "企业红名单", "/risk/api/execute2.do", ""),
        CODE_949("949", "企业评价信息", "/risk/api/execute2.do", ""),
        CODE_943("943", "企业失信核查", "/risk/api/execute2.do", ""),
        CODE_944("944", "企业处罚核查", "/risk/api/execute2.do", ""),
        CODE_957("957", "企业法人信息核验", "/jxdata/api/auth/jm/execute2.do", ""),
        CODE_960("960", "企业信息核验", "/jxdata/api/auth/jm/execute2.do", ""),
        CODE_953("953", "小微企业查询", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_951("951", "网站备案信息查询", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_955("955", "税务评级查询", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_954("954", "组织机构信息查询", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_950("950", "企业族谱查询", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_962("962", "企业名称模糊查询", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_961("961", "附近公司信息查询", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_956("956", "商圈企业信息查询", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_958("958", "企业信息查询(照面)", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_965("965", "企业信息验证(三要素)", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_966("966", "企业法人信息验证(四要素)", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_959("959", "企业信息查询(增值)", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_952("952", "企业人员信息查询", "/jxdata/api/auth/company/execute2.do", ""),
    
        CODE_971("971", "企业人员信息查询", "/jxdata/api/auth/company/execute2.do", ""),
    
        CODE_973("973", "IP风险核查", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_974("974", "IP归属地核查-IPv4版", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_975("975", "IP归属地核查-IPv6版", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_970("970", "工商风险核查报告", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_977("977", "电信诈骗识别-商户版", "/risk/api/execute2.do", ""),
        CODE_978("978", "涉金融风险黑名单-商户版", "/risk/api/execute2.do", ""),
        CODE_979("979", "电子营业执照申请授权二维码", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_980("980", "电子营业执照验证授权二维码", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_981("981", "电子营业执照执照信息查询", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_982("982", "电子营业执照执照有效性验证", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_983("983", "电子营业执照企业人员身份验证", "/jxdata/api/auth/company/execute2.do", ""),
        CODE_984("984", "电子营业执照持照人身份验证", "/jxdata/api/auth/company/execute2.do", ""),;
    
        private final String code;
        private final String desc;
        private final String url;
        private final String params; // 备用属性,暂时无用
    
        CFCAJINXINCodeEnum(String code, String desc, String url, String params) {
            this.code = code;
            this.desc = desc;
            this.url = url;
            this.params = params;
        }
    
        public String getCode() {
            return code;
        }
    
        public String getUrl() {
            return url;
        }
    
        public static String getUrl(String code) {
            for (CFCAJINXINCodeEnum codeEnum : values()) {
                if (codeEnum.getCode().equals(code)) {
                    return codeEnum.url;
                }
            }
            throw new RuntimeException("未知CFCA金信接口码:" + code);
        }
    
        public String getDesc() {
            return desc;
        }
    
        public static String getDesc(String code) {
            for (CFCAJINXINCodeEnum codeEnum : values()) {
                if (codeEnum.getCode().equals(code)) {
                    return codeEnum.desc;
                }
            }
            throw new RuntimeException("未知CFCA金信接口码:" + code);
        }
    
        public String getParams() {
            return params;
        }
    
        public static CFCAJINXINCodeEnum getByCode(String code) {
            if (code == null || "".equals(code)) {
                throw new NullPointerException("v 为空");
            }
            for (CFCAJINXINCodeEnum codeEnum : values()) {
                if (codeEnum.getCode().equals(code)) {
                    return codeEnum;
                }
            }
            throw new NullPointerException("没有此业务");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183

    2、CFCA金信反欺诈服务的公共参数配置

    import lombok.Data;
    
    import java.io.Serializable;
    
    /**
     * CFCA 金信反欺诈服务的公共参数配置dto
     *
     * @author:gan
     * @date: 2023-09-21 10:10
     */
    @Data
    public class CFCAJINXINConfig implements Serializable {
        //请求地址前缀部分,格式:http://192.168.1.223:8091
        private String host;
    
        //商户号
        private String merchNo;
    
        //金信平台公钥
        private String jinXinSM2PublicKey;
    
        //商户私钥
        private String merchSM2PrivateKey;
    
        //客户通信证书路径(证书需要申请)
        private String keystorePath;
    
        //客户通信证书密码
        private String keystorePassword;
    
        //金信通信证书信任库路径(证书由金信提供)
        private String truststorePath;
    
        //金信通信证书信任库密码
        private String truststorePassword;
    
        //接口业务编码
        private String transcode;
    
        //商户流水号
        private String ordersn;
    
        //商户订单号
        private String dsorderid;
    
        //api版本号
        private String version;
    
        //时间戳
        private String timestamp;
    
        //二级商户名称,例如:xxx商户名-yyy产品名称-zzz使用方法
        private String sCustomerName;
    
        //真实场景 见接口文档枚举
        private String sceneCode;
    
        //预留字段
        private String scUsePurpose;
    
        //预留字段
        private String protocolVerNm;
    
        //预留字段
        private String serialNm;
    
        //请求ip
        private String reqIp;
    
        //备注
        private String remark;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    3、加密解密工具类

    这是demo里面的

    import cfca.sadk.org.bouncycastle.util.encoders.Hex;
    import org.bouncycastle.asn1.gm.GMNamedCurves;
    import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
    import org.bouncycastle.asn1.x9.X9ECParameters;
    import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
    import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
    import org.bouncycastle.jce.provider.BouncyCastleProvider;
    import org.bouncycastle.jce.spec.ECParameterSpec;
    import org.bouncycastle.jce.spec.ECPrivateKeySpec;
    import org.bouncycastle.jce.spec.ECPublicKeySpec;
    import org.bouncycastle.math.ec.ECPoint;
    import org.bouncycastle.util.encoders.Base64;
    //import org.bouncycastle.util.encoders.Hex;
    
    import javax.crypto.BadPaddingException;
    import javax.crypto.Cipher;
    import javax.crypto.IllegalBlockSizeException;
    import javax.crypto.NoSuchPaddingException;
    import java.math.BigInteger;
    import java.security.*;
    import java.security.spec.ECGenParameterSpec;
    import java.security.spec.InvalidKeySpecException;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.Map;
    
    /**
     * bcprov-jdk15on 版本适用(1.61-1.68)
     */
    public class BC_SM2 {
    
        private BouncyCastleProvider provider;
        private X9ECParameters parameters;
        private ECParameterSpec ecParameterSpec;
        private KeyFactory keyFactory;
    
        static {
            if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
                System.out.println("security provider BC not found");
                // 注意:注册BouncyCastle是通过下面的语句实现的。注册只需要在启动时进行一次,后续就可以使用BouncyCastle提供的所有哈希算法和加密算法。
                Security.addProvider(new BouncyCastleProvider());
            }
        }
    
        public BC_SM2(){
            try {
                provider = new BouncyCastleProvider();
                parameters = GMNamedCurves.getByName("sm2p256v1");
                ecParameterSpec = new ECParameterSpec(parameters.getCurve(),
                        parameters.getG(), parameters.getN(), parameters.getH());
                keyFactory = KeyFactory.getInstance("EC", provider);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 生成密钥对
         */
        public KeyPair generateSm2KeyPair() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException {
            final ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");
            // 获取一个椭圆曲线类型的密钥对生成器
            final KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", provider);
            SecureRandom random = new SecureRandom();
            // 使用SM2的算法区域初始化密钥生成器
            kpg.initialize(sm2Spec, random);
            // 获取密钥对
            KeyPair keyPair = kpg.generateKeyPair();
            return keyPair;
        }
    
        /**
         * 加密
         *
         * @param input  待加密文本
         * @param pubKey 公钥
         */
        public String encode(String input, String pubKey)
                throws NoSuchPaddingException, NoSuchAlgorithmException,
                BadPaddingException, IllegalBlockSizeException,
                InvalidKeySpecException, InvalidKeyException {
            // 获取SM2相关参数
            X9ECParameters parameters = GMNamedCurves.getByName("sm2p256v1");
            // 椭圆曲线参数规格
            ECParameterSpec ecParameterSpec = new ECParameterSpec(parameters.getCurve(), parameters.getG(), parameters.getN(), parameters.getH());
            // 将公钥HEX字符串转换为椭圆曲线对应的点
            ECPoint ecPoint = parameters.getCurve().decodePoint(Hex.decode(pubKey));
            // 获取椭圆曲线KEY生成器
            KeyFactory keyFactory = KeyFactory.getInstance("EC", provider);
            BCECPublicKey key = (BCECPublicKey) keyFactory.generatePublic(new ECPublicKeySpec(ecPoint, ecParameterSpec));
            // 获取SM2加密器
            Cipher cipher = Cipher.getInstance("SM2", provider);
            // 初始化为加密模式
            cipher.init(Cipher.ENCRYPT_MODE, key);
            // 加密并编码为base64格式
            return cfca.sadk.org.bouncycastle.util.encoders.Base64.toBase64String(cipher.doFinal(input.getBytes()));
        }
    
        /**
         * 解密
         *
         * @param input  待解密文本
         * @param prvKey 私钥
         */
        public String decoder(String input, String prvKey) throws NoSuchPaddingException, NoSuchAlgorithmException,
                InvalidKeySpecException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
            // 获取SM2加密器
            Cipher cipher = Cipher.getInstance("SM2", provider);
            // 将私钥HEX字符串转换为X值
            BigInteger bigInteger = new BigInteger(prvKey, 16);
            BCECPrivateKey privateKey = (BCECPrivateKey) keyFactory.generatePrivate(new ECPrivateKeySpec(bigInteger,
                    ecParameterSpec));
            // 初始化为解密模式
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            // 解密
            //return cipher.doFinal(Base64.decode(input));
            return new String(cipher.doFinal(org.bouncycastle.util.encoders.Base64.decode(input)));
        }
    
        /**
         * 签名
         *
         * @param plainText 待签名文本
         * @param prvKey    私钥
         */
        public String sign(String plainText, String prvKey) throws NoSuchAlgorithmException, InvalidKeySpecException,
                InvalidKeyException, SignatureException {
            // 创建签名对象
            Signature signature = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), provider);
            // 将私钥HEX字符串转换为X值
            BigInteger bigInteger = new BigInteger(prvKey, 16);
            BCECPrivateKey privateKey = (BCECPrivateKey) keyFactory.generatePrivate(new ECPrivateKeySpec(bigInteger,
                    ecParameterSpec));
            // 初始化为签名状态
            signature.initSign(privateKey);
            // 传入签名字节
            signature.update(plainText.getBytes());
            // 签名
            return org.bouncycastle.util.encoders.Hex.toHexString(signature.sign());
        }
    
        /**
         * 根据 map 的 value、key排序拼接,生成待签名字符串,并签名
         */
        public String sign(Map<String, Object> map, String prvKey) throws NoSuchAlgorithmException, InvalidKeySpecException,
                InvalidKeyException, SignatureException {
            return sign(getSortStr(map), prvKey);
        }
    
        public String getSortStr(Map<String, Object> sortedParams) {
            StringBuilder signSrc = new StringBuilder();
            List<String> keys = new ArrayList<String>(sortedParams.keySet());
            Collections.sort(keys);
            for (String key : keys) {
                Object value = sortedParams.get(key);
                if (key != null && !"".equals(key) && value != null && !"sign".equals(key)) {
                    signSrc.append(key).append("=").append(value);
                }
            }
            return signSrc.toString();
        }
    
        /**
         * 验证签名
         */
        public boolean verify(String plainText, String signatureValue, String pubKey) throws NoSuchAlgorithmException, InvalidKeySpecException,
                InvalidKeyException, SignatureException {
            Signature signature = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), provider);
            ECPoint ecPoint = parameters.getCurve().decodePoint(Hex.decode(pubKey));
            BCECPublicKey key = (BCECPublicKey) keyFactory.generatePublic(new ECPublicKeySpec(ecPoint, ecParameterSpec));
            signature.initVerify(key);
            signature.update(plainText.getBytes());
            return signature.verify(Hex.decode(signatureValue));
        }
    
        public static void main(String[] args) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException {
            System.out.println("----------------------jdk版本信息----------------------------------------------------------");
            System.out.println(System.clearProperty("java.version"));
            System.out.println(System.clearProperty("os.name"));
    
            System.out.println("----------------------测试 开始----------------------------------------------------------");
            String str = "test encode";
            BC_SM2 sm2 = new BC_SM2();
            KeyPair keyPair = sm2.generateSm2KeyPair();
            BCECPrivateKey privateKey = (BCECPrivateKey) keyPair.getPrivate();
            BCECPublicKey publicKey = (BCECPublicKey) keyPair.getPublic();
    
            // 拿到密钥
            String pubKey =  "04" + (publicKey.getQ().getXCoord() + "" + publicKey.getQ().getYCoord());
            String prvKey = privateKey.getD().toString(16);
    
            System.out.println("Private Key: " + prvKey);
            System.out.println("Public Key: " + pubKey);
    
            // 加解密测试
            try {
                System.out.println("加密前:" + str);
                String encode = sm2.encode(str, pubKey);
                System.out.println("加密后:" + encode);
                String decoder = new String(sm2.decoder(encode, prvKey));
                System.out.println("解密后:" + decoder);
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("加解密错误");
            }
            // 签名和验签测试
            try {
                System.out.println("签名源数据:" + str);
                String signStr = sm2.sign(str, prvKey);
                System.out.println("签名后数据:" + signStr);
                boolean verify = sm2.verify(str, signStr, pubKey);
                System.out.println("签名验证结果:" + verify);
            } catch (Exception e) {
                System.out.println("签名和验签错误");
            }
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219

    4、请求参数dto

    调哪个接口就创建对应的请求参数的dto,我这个是三要素核验的dto。因为在工具类中用反射动态设置参数,属性名称必须与请求参数名称保持一致。

    package org.jeecg.modules.cfca.dto;
    
    import lombok.Data;
    
    /**
     * 运营商风险识别dto
     *
     * 验证用户手机号码与姓名、证件号码与运营商实名预留信息是否一致,不一致的情况下返回具体要素不符
     *
     * @author:gan
     * @date: 2023-09-21 10:17
     */
    @Data
    public class CFCAJINXINCode209ReqDto {
    
        //姓名
        private String username;
    
        //手机号码
        private String mobile;
    
        //身份证号
        private String idcard;
    
        //证件类型,只支持身份证,传 01
        private String idtype;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    5、调用接口工具类(关键是这个)

    import com.alibaba.fastjson.JSONObject;
    import com.alibaba.fastjson.serializer.SerializerFeature;
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    import org.jeecg.common.util.VerifyUtil;
    import org.jeecg.modules.cfca.dto.CFCAJINXINCode209ReqDto;
    import org.jeecg.modules.cfca.dto.CFCAJINXINConfig;
    import org.jeecg.modules.cfca.enums.CFCAJINXINCodeEnum;
    
    import javax.net.ssl.*;
    import java.io.*;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.net.URLConnection;
    import java.security.KeyStore;
    import java.util.*;
    
    /**
     * CFCA金信反欺诈服务工具类
     *
     * @author:gan
     * @date: 2023-09-21 09:53
     */
    public class CFCAJINXINUtils {
    
        private static Logger log = LogManager.getLogger();
    
        private static BC_SM2 bcSm2 = new BC_SM2();  //加密解密需要用到
    
        private final static String GET_STR = "get";  //get方法字符
        private final static String GET_CLASS_STR = "getClass";  //getClass方法字符
    
        private static StringBuilder host; // 请求地址前缀部分,格式:http://192.168.1.223:8091
        private static String jinXinSM2PublicKey; // 金信平台公钥
        private static String merchSM2PrivateKey; // 商户私钥
        private static String keystorePath; // 客户通信证书路径(证书需要申请)
        private static String keystorePassword; // 客户通信证书密码
        private static String truststorePath; // 金信通信证书信任库路径(证书由金信提供)
        private static String truststorePassword; // 金信通信证书信任库密码
        private static String fullUrl; // 当前接口业务完整地址
    
        /**
         * 初始化CFCA金信反欺诈服务需要的配置
         * @param config
         */
        private static void init(CFCAJINXINConfig config) {
            VerifyUtil.checkParam(config, "CFCA金信反欺诈服务请求参数为空!");
    
            List<String> noEmptyFieldList = Arrays.asList("host", "merchNo", "jinXinSM2PublicKey", "merchSM2PrivateKey", "keystorePath", "keystorePassword", "truststorePath", "truststorePassword", "transcode", "version");
            VerifyUtil.checkBeanByNonEmptyFiledList(config, noEmptyFieldList, "CFCA金信反欺诈服务请求参数");
    
            host = new StringBuilder();
            host.append(config.getHost());
            int length = host.length();
            if (host.indexOf("/", length - 1) > 0) {  //最后一个字符为“/”,则去掉
                host = host.delete(length - 1, length);
            }
    
            fullUrl = host.append(CFCAJINXINCodeEnum.getUrl(config.getTranscode())).toString();
    
            jinXinSM2PublicKey = config.getJinXinSM2PublicKey();
            merchSM2PrivateKey = config.getMerchSM2PrivateKey();
            keystorePath = config.getKeystorePath();
            keystorePassword = config.getKeystorePassword();
            truststorePath = config.getTruststorePath();
            truststorePassword = config.getTruststorePassword();
        }
    
        /**
         * 发送CFCA金信反欺诈服务请求
         * @param config
         * @param params
         */
        public static JSONObject sendRequest(CFCAJINXINConfig config, Object params) throws Exception {
            init(config);
    
            // 请求最外层参数,所有接口公共参数
            Map<String, Object> reqMap = new HashMap<>();
            reqMap.put("transcode", config.getTranscode());  // 接口业务编码,具体业务请参考接口文档
            reqMap.put("merchno", config.getMerchNo());  // 商户号
            reqMap.put("ordersn", UUID.randomUUID().toString().replaceAll("-", ""));  //商户流水号
            reqMap.put("dsorderid", UUID.randomUUID().toString().replaceAll("-", ""));  //商户订单号
            reqMap.put("version", config.getVersion());  //api版本号
            reqMap.put("timestamp", System.currentTimeMillis());  // 时间戳
    
            // 请求内层 data 参数,所有接口公共参数
            Map<String, Object> data = new HashMap<>();
            data.put("sCustomerName", config.getSCustomerName()); // 二级商户名称,例如:xxx商户名-yyy产品名称-zzz使用方法
            data.put("sceneCode", config.getSceneCode());   //真实场景 见接口文档枚举
            data.put("scUsePurpose", config.getScUsePurpose()); // 预留字段
            data.put("protocolVerNm", config.getProtocolVerNm()); // 预留字段
            data.put("serialNm", config.getSerialNm()); // 预留字段
            data.put("reqIp", config.getReqIp());  // 请求ip
            data.put("remark", config.getRemark()); // 备注
    
            //这里设置的是不同接口独有的参数,也是data中的参数
            setInterfaceSpecificParams(params, data);
    
            return request(reqMap, data);
        }
    
        /**
         * 设置不同接口特定参数
         * @param params  传入请求参数
         * @param data   调用接口请求参数
         */
        private static void setInterfaceSpecificParams(Object params, Map<String, Object> data) {
            Method[] methods = params.getClass().getMethods();
            Method currentMethod = null;  //当前方法
            String currentMethodName = null;  //当前方法名称
            StringBuilder currentPropertyName = new StringBuilder();  //当前属性名称
    
            for (int i = 0; i < methods.length; i++) {
                currentMethod = methods[i];
    
                currentMethodName = currentMethod.getName();
                if (GET_CLASS_STR.equals(currentMethodName)) {  //getClass方法不为请求参数
                    continue;
                }
    
                if (currentMethodName.startsWith(GET_STR) && currentMethodName.length() > 3) {
                    currentPropertyName.append(currentMethodName.substring(3));  //截取currentMethodName字符串的中索引第四到最后的字符,也就是去掉“get”字符
                    currentPropertyName.replace(0, 1, currentPropertyName.substring(0, 1).toLowerCase());  //将首字符的大写改为小写
    
                    try {
                        data.put(currentPropertyName.toString(), currentMethod.invoke(params));
    
                        currentPropertyName.delete(0, currentPropertyName.length());  //设置完参数就清除StringBuilder中的数据,给下一个参数设置时用
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
    
                        throw new IllegalArgumentException(e);
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
    
                        throw new RuntimeException(e.getMessage());
                    }
                }
            }
        }
    
        /**
         * 处理请求参数并发送请求
         * @param reqMap  外层请求参数
         * @param data   data请求参数
         * @throws Exception
         */
        private static JSONObject request(Map<String, Object> reqMap, Map<String, Object> data) throws Exception {
            //reqMap的值转化为字符
            String reqMapToStr = JSONObject.toJSONString(reqMap, SerializerFeature.WriteNonStringValueAsString);
    
            //data的值转化为字符
            String dataToStr = JSONObject.toJSONString(data, SerializerFeature.WriteNonStringValueAsString);
    
            //获取【金信平台】SM2 公钥,对 data 使用 SM2 进行加密
            reqMap.put("data", bcSm2.encode(dataToStr, jinXinSM2PublicKey));
    
            // 对请求报文生成签名信息
            reqMap.put("sign", getSign(reqMap, merchSM2PrivateKey));
    
            log.info("请求地址:{}" , fullUrl);
            log.info("请求参数:{}" , dataToStr);
            log.info("完整的请求参数:{}" , reqMapToStr);
    
            String result = post(fullUrl, JSONObject.toJSONBytes(reqMap), keystorePath, keystorePassword, truststorePath, truststorePassword); // 发起请求
    
            return response(result);
        }
    
        /**
         * 处理响应
         * @param response  响应结果字符
         * @throws Exception
         */
        private static JSONObject response(String response) throws Exception {
            log.info("响应参数:{}" , response);
    
            JSONObject respJson = JSONObject.parseObject(response);
            String sortStr = bcSm2.getSortStr(respJson);
            Object respSign = respJson.get("sign");
            Object respData = respJson.get("data");
            JSONObject decryptDataJsonObj = null;  //转化为JSONObject的data数据
            if (VerifyUtil.isNotEmpty(respSign) && bcSm2.verify(sortStr, String.valueOf(respSign), jinXinSM2PublicKey)) {
    
                if (VerifyUtil.isNotEmpty(respData)) {
                    String decryptData = bcSm2.decoder(respData.toString(), merchSM2PrivateKey); // 解密
                    decryptDataJsonObj = JSONObject.parseObject(decryptData);
                    respJson.put("data", decryptDataJsonObj); // 后续可根据实际情况替换Object.class
    
                    log.info("解密响应参数:{}" , JSONObject.toJSONString(respJson, SerializerFeature.WriteNonStringValueAsString));
                    log.info("响应参数中的data:{}" , decryptData);
                } else {
                    log.info("【data】接口没有返回或为空");
                }
            } else {
                log.info("验签失败");
            }
    
            return respJson;
        }
    
        /**
         * 判断是否请求成功
         * @param respJson
         */
        public static boolean isSuccess(JSONObject respJson) {
            return "001000000".equals(respJson.getString("platformCode"));  //001000000代表接口调用成功,因为只有这里用到,每个接口的返回参数又不一样,不再用枚举类
        }
    
        /**
         * 接口调用失败时抛出错误信息
         * @param respJson
         */
        public static void getErrorInfo(JSONObject respJson) {
            if (!isSuccess(respJson)) {
                throw new RuntimeException(respJson.getString("platformDesc"));
            }
        }
    
        /**
         * 发送HTTP请求分类判断
         * @param url  请求路径
         * @param request  转化为byte数组后的请求参数
         * @param keystorePath  通信证书路径
         * @param keystorePassword  通信证书密码
         * @param truststorePath  金信通信证书信任库路径
         * @param truststorePassword  金信通信证书信任库密码
         * @return
         * @throws Exception
         */
        private static String post(String url, byte[] request, String keystorePath, String keystorePassword, String truststorePath, String truststorePassword) throws Exception {
            if (url.startsWith("https://")) {
                return doPostHttps(url, request, keystorePath, keystorePassword, truststorePath, truststorePassword);
            } else {
                return doPostHttp(url, request);
            }
        }
    
        /**
         * 发送HTTP请求,主要就是这个
         * @param reqUrl  请求路径
         * @param reqData  转化为byte数组后的请求参数
         * @param keystorePath  通信证书路径
         * @param keystorePassword  通信证书密码
         * @param truststorePath  金信通信证书信任库路径
         * @param truststorePassword  金信通信证书信任库密码
         * @return
         * @throws Exception
         */
        private static String doPostHttps(String reqUrl, byte[] reqData, String keystorePath, String keystorePassword, String truststorePath, String truststorePassword) throws Exception {
            OutputStream outputStream = null;
            BufferedReader bufferedReader = null;
            HttpsURLConnection httpsURLConnection = null;
            try {
                // 初始化密钥库
                KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
                KeyStore keyStore = getKeyStore(keystorePath, keystorePassword);
                keyManagerFactory.init(keyStore, keystorePassword.toCharArray());
    
                // 初始化信任库
                TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
                KeyStore trustKeyStore = getKeyStore(truststorePath, truststorePassword);
                trustManagerFactory.init(trustKeyStore);
    
                // 初始化SSL上下文
                SSLContext ctx = SSLContext.getInstance("SSL");
                ctx.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
                SSLSocketFactory sf = ctx.getSocketFactory();
    
                URL url = new URL(reqUrl);
    
                httpsURLConnection = (HttpsURLConnection) url.openConnection();
                httpsURLConnection.setSSLSocketFactory(sf);
    
                //设置链接超时时间
                httpsURLConnection.setConnectTimeout(60000);
    
                //设置读取超时时间
                httpsURLConnection.setReadTimeout(60000);
    
                // 设置是否向httpUrlConnection输出,因为这个是post请求,参数要放在
                // http正文内,因此需要设为true, 默认情况下是false;
                httpsURLConnection.setDoOutput(true);
    
                // 设置是否从httpUrlConnection读入,默认情况下是true;
                httpsURLConnection.setDoInput(true);
    
                // Post 请求不能使用缓存
                httpsURLConnection.setUseCaches(false);
    
                // 设定传送的内容类型是json UTF-8
                httpsURLConnection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
    
                // 设定请求的方法为"POST",默认是GET
                httpsURLConnection.setRequestMethod("POST");
    
                // 此处getOutputStream会隐含的进行connect(即:如同调用上面的connect()方法,
                // 所以在开发中不调用上述的connect()也可以)。
                outputStream = httpsURLConnection.getOutputStream();
    
                // 向对象输出流写出数据,这些数据将存到内存缓冲区中
                outputStream.write(reqData);
    
                // 刷新对象输出流,将任何字节都写入潜在的流中(些处为ObjectOutputStream)
                outputStream.flush();
    
                // 调用HttpURLConnection连接对象的getInputStream()函数,
                // 将内存缓冲区中封装好的完整的HTTP请求电文发送到服务端。
                // httpsURLConnection.getInputStream() 注意,实际发送请求的代码段就在这里
                bufferedReader = new BufferedReader(new InputStreamReader(httpsURLConnection.getInputStream(), "UTF-8"));
    
                StringBuilder jsonString = new StringBuilder();
                String line = "";
                while ((line = bufferedReader.readLine()) != null) {
                    jsonString.append(line);
                }
                return jsonString.toString();
            } catch (Exception e) {
                e.printStackTrace();
                log.error(e.getMessage(), e);
                return null;
            } finally {
                if (outputStream != null) {
                    outputStream.close();
                }
    
                if (bufferedReader != null) {
                    bufferedReader.close();
                }
    
                if(httpsURLConnection != null) {
                    httpsURLConnection.disconnect();
                }
            }
    
        }
    
        // 备用方法
        private static String doPostHttp(String urlStr, byte[] request) throws Exception {
            DataOutputStream objOutputStrm = null;
            OutputStream outStrm = null;
            InputStream inStrm = null;
            try {
                URL url = new URL(urlStr);
                // 此处的urlConnection对象实际上是根据URL的 // 请求协议(此处是http)生成的URLConnection类 // 的子类HttpURLConnection,故此处最好将其转化 // 为HttpURLConnection类型的对象,以便用到 // HttpURLConnection更多的API.如下:
                URLConnection rulConnection = url.openConnection();
                HttpURLConnection httpUrlConnection = (HttpURLConnection) rulConnection;
                //设置链接超时时间
                httpUrlConnection.setConnectTimeout(6000000);
                //设置读取超时时间
                httpUrlConnection.setReadTimeout(6000000);
                // 设置是否向httpUrlConnection输出,因为这个是post请求,参数要放在
                // http正文内,因此需要设为true, 默认情况下是false;
                httpUrlConnection.setDoOutput(true);
                // 设置是否从httpUrlConnection读入,默认情况下是true;
                httpUrlConnection.setDoInput(true);
                // Post 请求不能使用缓存
                httpUrlConnection.setUseCaches(false);
                // 设定传送的内容类型是可序列化的java对象
                // (如果不设此项,在传送序列化对象时,当WEB服务默认的不是这种类型时可能抛java.io.EOFException)
                httpUrlConnection.setRequestProperty("Content-type", "application/json");
                // 设定请求的方法为"POST",默认是GET
                httpUrlConnection.setRequestMethod("POST");
                // 此处getOutputStream会隐含的进行connect(即:如同调用上面的connect()方法,
                // 所以在开发中不调用上述的connect()也可以)。
                outStrm = httpUrlConnection.getOutputStream();
                // 现在通过输出流对象构建对象输出流对象,以实现输出可序列化的对象。
                objOutputStrm = new DataOutputStream(outStrm);
                // 向对象输出流写出数据,这些数据将存到内存缓冲区中
                objOutputStrm.write(request);
                // 刷新对象输出流,将任何字节都写入潜在的流中(些处为ObjectOutputStream)
                objOutputStrm.flush();
                // 调用HttpURLConnection连接对象的getInputStream()函数,
                // 将内存缓冲区中封装好的完整的HTTP请求电文发送到服务端。
                inStrm = httpUrlConnection.getInputStream(); // <===注意,实际发送请求的代码段就在这里
                BufferedReader read = new BufferedReader(new InputStreamReader(inStrm, "UTF-8"));
                StringBuffer jsonString = new StringBuffer();
                String line = "";
                while ((line = read.readLine()) != null) {
                    jsonString.append(line);
                }
                String json = jsonString.toString();
    
                return json;
            } catch (Exception e) {
                e.printStackTrace();
                log.error(e.getMessage(), e);
                return null;
            } finally {
                if (objOutputStrm != null)
                    objOutputStrm.close();
    
                if (outStrm != null) {
                    outStrm.close();
                }
    
                if (inStrm != null)
                    inStrm.close();
            }
        }
    
        /**
         * 获得KeyStore
         */
        private static KeyStore getKeyStore(String keyStorePath, String password) throws Exception {
            FileInputStream is = new FileInputStream(keyStorePath);
            KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
            ks.load(is, password.toCharArray());
            is.close();
            return ks;
        }
    
        /**
         * 使用私钥加签
         * @param reqParams 待加签的map集合,会对此map集合的key做排序,遍历拼接
         * @param merchSM2PrivateKey 商户私钥
         */
        private static String getSign(Map<String, Object> reqParams, String merchSM2PrivateKey) throws Exception {
            StringBuilder signSrc = new StringBuilder();
            List<String> keys = new ArrayList<>(reqParams.keySet());
            Collections.sort(keys);
            for (String key : keys) {
                Object value = reqParams.get(key);
                if (key != null && !"".equals(key) && value != null && !"sign".equals(key)) {
                    signSrc.append(key).append("=").append(value);
                }
            }
            String sign = bcSm2.sign(signSrc.toString(), merchSM2PrivateKey);
            return sign;
        }
    
        public static void main(String[] args) {
            CFCAJINXINConfig config = new CFCAJINXINConfig();
    
            config.setHost("请求地址前缀部分");
            config.setKeystorePath("客户通信证书路径(证书需要申请)");
            config.setKeystorePassword("客户通信证书密码");
            config.setTruststorePath("金信通信证书信任库路径(证书由金信提供)");
            config.setTruststorePassword("金信通信证书信任库密码");
            config.setMerchSM2PrivateKey("商户私钥");
            config.setJinXinSM2PublicKey("金信平台公钥");
            config.setMerchNo("商户号");
            config.setVersion("api版本号");
    
            config.setTranscode(CFCAJINXINCodeEnum.CODE_209.getCode());
    
            CFCAJINXINCode209ReqDto params = new CFCAJINXINCode209ReqDto();
            params.setUsername("你的名字");
            params.setMobile("你的手机号码");
            params.setIdcard("你的身份证号码");
            params.setIdtype("01");
    
            try {
                sendRequest(config, params);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426
    • 427
    • 428
    • 429
    • 430
    • 431
    • 432
    • 433
    • 434
    • 435
    • 436
    • 437
    • 438
    • 439
    • 440
    • 441
    • 442
    • 443
    • 444
    • 445
    • 446
    • 447
    • 448
    • 449
    • 450
    • 451
    • 452
    • 453
    • 454
    • 455
    • 456
    • 457
    • 458
    • 459
    • 460
    • 461
    • 462

    其中VerifyUtil是之前写的一个判空的工具类,感兴趣可以前往查看,也可以用自己的方式判空。

  • 相关阅读:
    webpack
    docker for windonws--Windows 10 家庭中文版 21H2 安装Docker Desktop初体验
    【LeetCode】206. 反转链表
    [云原生2.] Kurbernetes资源管理 ---- (陈述式资源管理方式)
    Linux -- 正则表达式基础
    阿里云4核8G服务器优惠价格表,最低价格501.90元6个月、983.80元1年
    mysql的约束
    Java日期格式
    万字指针超详细总结
    Git基本操作(超详细)
  • 原文地址:https://blog.csdn.net/studio_1/article/details/133173293