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

news2024/12/26 10:41:24

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

  • 一、准备
  • 二、调用接口
    • 1、查询接口文档
    • 2、查看代码示例
    • 3、测试调用接口
  • 三、工具类
    • 1、CFCA金信反欺诈服务接口码枚举类
    • 2、CFCA金信反欺诈服务的公共参数配置
    • 3、加密解密工具类
    • 4、请求参数dto
    • 5、调用接口工具类(关键是这个)

一、准备

之前对接过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": ""
	}
}

然后是响应参数

在这里插入图片描述

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

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

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); // 调用此方法设置拼接请求地址、生成签名信息、对数据进行加密,后发起请求、解密响应数据。
    }

}

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

在这里插入图片描述

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

在这里插入图片描述

“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、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("没有此业务");
    }
}

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;
}

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("签名和验签错误");
        }
    }

}

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;
}

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();
        }
    }
}

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1038705.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【N年测试总结】证券行业的测试特点

每个行业由于其业务形式&#xff0c;产品形态&#xff0c;行业要求等等的不同&#xff0c;都有其不同于其他行业的测试特点&#xff0c;对测试人员的重点能力要求也不同。 一、证券行业业务系统简介 证券行业的业务系统这里按照C端系统和B端业务系统两大类进行介绍。 C端系统…

tensorrt C++推理

char* trtModelStream{ nullptr }; //char* trtModelStreamnullptr; 开辟空指针后 要和new配合使用&#xff0c;比如89行 trtModelStream new char[size]size_t size{ 0 };//与int固定四个字节不同有所不同,size_t的取值range是目标平台下最大可能的数组尺寸,一些平台下size_…

通讯网关软件012——利用CommGate X2OPC实现MS SQL数据写入OPC Server

本文推荐利用CommGate X2OPC实现从MS SQL服务器获取数据并写入OPC Server。CommGate X2OPC是宁波科安网信开发的网关软件&#xff0c;软件可以登录到网信智汇(http://wangxinzhihui.com)下载。 【案例】如下图所示&#xff0c;实现从MS SQL数据库获取数据并写入OPC Server。 【…

(Vue2)智慧商城项目

新增两个目录api、utils api接口模块&#xff1a;发送ajax请求的接口模块 utils工具模块&#xff1a;自己封装的一些工具方法模块 第三方组件库vant-ui PC端&#xff1a;element-ui&#xff08;element-plus&#xff09; ant-design-vue 移动端&#xff1a;vant-ui Mint UI…

Vue3最佳实践 第五章 Vue 组件应用 3( Slots )

5.4 Slots 我们已经了解到组件能够接收任意类型的 JavaScript 值作为 props&#xff0c;但组件要如何接收模板内容呢&#xff1f;在某些场景中&#xff0c;我们可能想要为子组件传递一些模板片段&#xff0c;让子组件在它们的组件中渲染这些片段。Slots 可用于将Html内容从父组…

怎么样深入学习一门技术(Python)

进入官网 Python官网文档 https://docs.python.org/zh-cn/ 边敲代码边理解 多看教学视频 狠狠的花时间

Android 使用kotlin+注解+反射+泛型实现MVP架构

一&#xff0c;MVP模式的定义 ①Model&#xff1a;用于存储数据。它负责处理领域逻辑以及与数据库或网络层的通信。 ②View&#xff1a;UI层&#xff0c;提供数据可视化界面&#xff0c;并跟踪用户的操作&#xff0c;以便通知presenter。 ③Presenter&#xff1a;从Model层获…

Securing TEEs With Verifiable Execution Contracts【TDSC`23】

目录 摘要引言贡献 背景Intel SGX侧信道攻击Intel处理器的硬件扩展 概述威胁模型SGX已存的安全威胁侧信道泄露操作系统相关的威胁现有防御的限制 可验证的执行合同作为防御 摘要 最近的研究表明&#xff0c;可信执行环境&#xff0c;如Intel Software Guard Extensions&#x…

Nginx 背锅解析漏洞

Nginx 背锅解析漏洞 文章目录 Nginx 背锅解析漏洞1 在线漏洞解读:2 环境搭建3 影响版本&#xff1a;4 漏洞复现4.1 访问页面4.2 上传文件 4.3 上传失败4.4 使用bp进行分析包4.5 对返回图片位置进行访问4.6 执行php代码技巧-图片后缀加./php4.7 分析原因 --》cgi.fix_pathinfo--…

工艺防错指导、可视化工具管理——SunTorque智能扭矩系统

智能扭矩系统-智能拧紧系统-智能扭矩控制-SunTorque 拧紧的定义——运用拧紧工具及螺栓&#xff0c;使被联接体紧密贴合&#xff0c;并能承受一定的载荷&#xff0c;且被连接体间具备足够的夹紧力&#xff0c;以确保被联接零件的可靠联接和正常工作。 从定义中前六个字“运用…

解读:ISO 14644-21:2023《洁净室及相关受控环境:悬浮粒子采样》发布指导粒子采样!

药品洁净实验室环境监测结果是否满足微生物检测需求&#xff0c;直接决定检测结果的有效性准确性&#xff0c;进行药品微生物检测&#xff0c;必须对实验环境进行日常和定期监测&#xff0c;其内容包括非生物活性的空气悬浮粒子数及有生物活性的微生物监测。 悬浮粒子监测是保证…

python百钱百鸡

编写程序&#xff0c;解决“百钱百鸡”问题。 一只公鸡值五钱&#xff0c;一只母鸡值三钱&#xff0c;三只小鸡值一钱。 源代码&#xff1a; for a in range(1, 101): for b in range(1, 101): for c in range(1, 101): if (a * 5 b * 3 c / 3 100)…

CSRF攻击

防御策略 过滤判断换referer头&#xff0c;添加tocken令牌验证&#xff0c;白名单 CSRF攻击和XSS比较 相同点&#xff1a;都是欺骗用户 不同点&#xff1a; XSS有攻击特征&#xff0c;所有输入点都要考虑代码&#xff0c;单引号过滤 CSRF没有攻击特征&#xff0c;利用的点…

城市智慧公厕:引领科技创新的新时代

城市智慧公厕已经成为当下社会治理模式的升级范式&#xff0c;催生了无限的科技创新。如智慧公厕源头厂家广州中期科技有限公司&#xff0c;所推出的智慧公厕整体解决方案&#xff0c;除基本的厕位监测与引导、环境监测与调节、安全防范与管理、保洁考勤管理、多媒体交互、综合…

数字化转型的五个等级及思考

数字化转型是当前企业和社会关注的热点话题。然而&#xff0c;对于数字化转型的五个等级及其思考&#xff0c;并没有一个清晰的概述。以下是我对数字化转型的五个等级及其思考的简要探讨。 第一等级&#xff1a;基础设施升级 在数字化转型的初始阶段&#xff0c;企业需要对其基…

长期用眼不再怕!NineData SQL 窗口支持深色模式

您有没有尝试过被明亮的显示器闪瞎眼的经历&#xff1f; 在夜间或低光环境下&#xff0c;明亮的界面会导致许多用眼健康问题&#xff0c;例如长时间使用导致的眼睛疲劳、干涩和不适感&#xff0c;同时夜间还可能会抑制褪黑素分泌&#xff0c;给您的睡眠质量带来影响。 这些问…

uni-app:实现picker下拉列表

效果 代码 <template><view class"container"><picker name"info" change"bindPickerChange9" :value"index9" :range"selectDatas9"><view class"right"><view class"right_l…

Nginx负载均衡详解

一、负载均衡介绍 1、负载均衡的定义 单体服务器解决不了并发量大的请求&#xff0c;所以&#xff0c;我们可以横向增加服务器的数量&#xff08;集群&#xff09;&#xff0c;然后将请求分发到各个服务器上&#xff0c;将原先请求集中到单个服务器上的情况改为将请求分发到多…

【EI会议】第三届大数据、人工智能与风险管理国际学术会议 (ICBAR 2023)

第三届大数据、人工智能与风险管理国际学术会议 (ICBAR 2023) 2023 3rd International Conference on Big Data, Artificial Intelligence and Risk Management 第三届大数据、人工智能与风险管理国际学术会议&#xff08;ICBAR2023&#xff09;将于2023年11月24-26日在中国成…