调用招商银行接口(手把手教程,附调用接口工具类)

一、下载示例代码

首先到招商银行的文档中心下载示例代码:
https://openbiz.cmbchina.com/developer/UI/Business/CloudDirectConnect/Public/DocumentCenter/DocDetail.aspx?bizkey=DCCT20201215143758097&fabizkey=1&treeID=100034594

在这里插入图片描述
在这里插入图片描述
这个下载下来是一个压缩包,打开压缩包,里面有一个,java文件这个文件就是示例代码了。

二、申请测试环境、或者生产环境加入到示例代码

在这里插入图片描述
这里面的公钥、私钥等环境参数是不能直接用的(红框框住的那些就是),需要企业事先跟招商银行申请测试环境,申请成功后将申请环境中的参数与示例代码的参数一一替换。

三、调用招商银行接口参数拼装

在这里插入图片描述
这个data参数里面的就是招商银行的接口参数,这样看有点费劲,格式一下json
在这里插入图片描述

注意,reqid是有格式要求的

在这里插入图片描述

这个接口的funcode是“NTDMAADD”,在文档中找到对应的接口详情
在这里插入图片描述
会有接口的使用说明,请求和响应的参数介绍,往下拉还会有调用示例和响应示例
在这里插入图片描述

四、调用接口测试

在调用接口前记得找管理网银的同事添加一下本机ip的白名单,否则会出现白名单错误,出现这个错误说明接口传参没问题了,就差白名单了。
在这里插入图片描述
添加了白名单之后,如果响应体中参数“resultcode”的值为“SUC0000”,说明接口调用成功,保存下需要的返回信息即可。

当然也可以用postman测试
在这里插入图片描述
但是“DATA”参数还是需要用示例代码中的方法加密,个人觉得意义不大。

五、最后,贴上我自己改造过的工具类

这个不是调用接口必要的,只是觉得经过我改动后调用接口方便一点

1、这是获取银行环境参数的工具类

import lombok.Data;

/**
 * 银行配置dto
 *
 * @author:user
 * @date: 2022-03-25 10:51
 */
@Data
public class BankConfigDto {

    //公钥
    private String pubkeyStr;

    //私钥
    private String prikeyStr;

    //AESKEY
    private String AESKEY;

    //用户id
    private String UID;

    //访问地址
    private String URL;

    //=============以下参数并不是所有的银行接口都需要,所以是非必填的

    //付方账号
    private String payacc;

    //模式编号
    private String busmod;

    //网银审批标志    可选;Y 直连经办,客户端审批
    private String grtflg;

    //分行号
    private String bbknbr;

}

2、新增记账子单元 NTDMAADD-参数dto

这只是这个接口的dto,后面要调用其他接口再增加对应的参数dto。


import lombok.Data;

/**
 * 新增记账子单元 NTDMAADD-参数dto
 *
 * @author:user
 * @date: 2022-03-25 18:14
 */
@Data
public class NTDMAADDParam {

    //账号
    private String accnbr;

    //记账子单元编号
    // 不超过 10 位
    private String dmanbr;

    //记账子单元名称
    private String dmanam;

    //=================以下字段为非必填

    //额度控制标志
    //空:默认 Y,Y:允许透支 N:不允许透支
    private String ovrctl;

    //退票处理方式
    //空:默认 N,Y: 退回原记账子单元 N:退回结算户
    private String bcktyp;

    //余额非零时是否可关闭
    //Y:可关闭, N:不可关闭 空:默认 Y
    private String clstyp;
}

3、这是调用银行接口的工具类,一般就用“commonRequestMethod”方法


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.text.SimpleDateFormat;
import java.util.*;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import org.apache.commons.lang.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.jeecg.common.util.ReflectHelper;
import org.jeecg.modules.cmb.dto.*;
import org.jeecg.modules.cmb.entity.CMBBase64;
import org.jeecg.modules.cmb.entity.CMBRequest;
import org.jeecg.modules.cmb.entity.CMBRestBuilder;
import org.jeecg.modules.cmb.enums.CMBTransAction;

/**
 * 调用银行接口工具类
 *
 * @author:user
 * @date: 2022-03-24 11:50
 */
public class CMBUtils {

    static String pubkeyStr = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5Ec7viMyQC5SShRz1jP0IQRLLVGDQ4f1rgZwtxT4ZOgnWxUoAHquj2yIrgFjNpWVnt/1dJGtXWkpp2UN3jMI5ubjVQkL0OFD+8r0IFXYAARsCLAwVLF0LE487KvVRaQC7A7rPlFfBtE/v++KajzMuDauNlIASYobcFKYdZ89vIfE/xMg/44QJqQ2XBkoMnJ7ul0kMdh4YWOQnO0qqvXD2eK3KPaXMRtxieGsVBgsvtETprw98bTl9tPUBUrneyirrccS8/Z6raV6nioyx2RzrMld8YnjlnV2YTJpNAlG+y/wLoKY55Rkjcvg9wSe8qbI/VtYVQfQz8gfeUzFQTKKCwIDAQAB";
    static String prikeyStr = "MIIEowIBAAKCAQEAwN7xTseqQs1pNA/+gTgXRqcxCYfkxDTsckfqf3O2ndsvJS5T" + "8Fb0oHTyjy0HjrKLASWWUKfhQGXPHbo1FQd+0TyHxSza55+HtXquUq7QsAITHCu3"
            + "U7aslvC7xe6/2E7nhu1TausF1nSyB1o4xVEjZyjrdQpTID0JvG8BtA5Yem9YDBCM" + "ZHBxvarQHVqdBsqo2G3M09qeUDbY3DuBgdiVAw0ApIM8mKGj4jsWmRSnypuxl40B"
            + "jWAr6Xgk44MpSGHndhiFXGvfMRRYEd8Z30w32QlB+Gjk8rQwXnvxG8YCBPYqXVkq" + "wld81bHFFz5zHQ0qekPhD8RrFimPn+RaD9VNfwIDAQABAoIBAQCxUUZQw0hJOkgq"
            + "UToO2t6rWjK/LGyp5m4rcuqoWl3eKxDhAgKxx4AHtPz7nM6B5qvdVg0oAfAZIICW" + "OAdtWgLBowC/yklHAWfm9O8nnQjHiGDBWX+mOx/ZdWFsy98cow5BAhfbCE/Jpme2"
            + "UsA2yC3gPcKbS+64iOVWlEfgnf/CLte/dE0eAMfsp5wXpwv3ygA4wtyd2X2P+y6s" + "+WYBKSvNMS08W4dsxwU9Q3AG3hS0Uab09qIPNS8tEMZ2L1tl0/VvkrAYjayM1CcK"
            + "CrSnwtH6eJVi4WQxL1K3QxyxDKucsOlqSHg++4VMpGZNpvstn3IsY3PyCgfsODvH" + "aoygvDBhAoGBAPxxdcI9stK9bIGSms0FqbVXYj8WmAjE/M9B7ECToWRQg65Gf8MY"
            + "PyUSkY2mbDHwf+yPsUb5Oli+a2GW8BwmJWeXEIy0lQxa1TS2b7CN6XJtZVnjEgiZ" + "d7bXy/j69r/C4CMlfbrRWnUGftKr/U7ynaGs10/bISeW12E7WdLV5+kDAoGBAMOW"
            + "nEzAFMPFzG9p/GtYpWU5jMNSiakzfm6n9Nwr7dFGnLhVGtO6act1bm/WB26NAgIE" + "ArhcitoKrI346nfkoZLXBpzzyJgFx4r31d1RN9Vsrt6AEywlwnLwHk2HXtCwmqre"
            + "hZ4I741S2rHlaT8ifNwLyjW2sbw9QnpC3RL7R3rVAoGAOI/Dbs4cLxO6KB4NCTrn" + "l3YI0VHiprRcYKPIp39sfel8V6P8JF5eZ5QNgMt1GotkXkCj298jr5aawLbs/aGe"
            + "Z+N1FdGwQ6BmfPUTeV+SmszgFI/IDp00MYeQcCzq9HRZfAZ+cUlPF0FpURKwIuxB" + "XWQ4qe/TMeeeQm7l5VOALrkCgYAljLa5LW9PHpxfD3P8j+pBAsl5flEbgN1XFTu3"
            + "QV/I+8t+wCgEWheRjhwDsI2AteWayXZUOsAVmFMEdrNdDTHP5SRJ4auzM/jZPzd5" + "4+vaN6Fi6ifEJAOu2VaX/9M+MYmgIFR6wLBs62k9GhQYoOBjxoetxENfJkuq+UdE"
            + "K6XPeQKBgFvf+SUrg7hFpRRyCq+DehdMQk1TJnEPTNLOalfrA/319KA8LGa0Q+ay" + "5c2mDc9F//yAJEAT1WTEqHnvKBQvjofFAGRntoCT8anAnskSytwwpltKqDcpoKx/"
            + "hVK+eVL47wuFroCBLGj0Zm3I7S+saGGmVllEky4jceE7IMTN7i6W";

    private static PrivateKey privateKey;
    private static PublicKey publicKey;
    static String UID = "N002985759";

    static String URL = "http://99.12.250.6:9080/cdcserver/api/v2";
    static String AESKEY = "YSqdwE8vAQ1BcfYCpESUsnVzOOMA2ZSd";

    public static void main(String[] args) throws Exception {

        // TODO Auto-generated method stub
        try {
            BankConfigDto bankConfigDto = new BankConfigDto();
            bankConfigDto.setPubkeyStr(pubkeyStr);
            bankConfigDto.setPrikeyStr(prikeyStr);
            bankConfigDto.setAESKEY(AESKEY);
            bankConfigDto.setUID(UID);
            bankConfigDto.setURL(URL);

//            //============================新增记账子单元 接口测试================================
            NTDMAADDParam ntdmaaddParam = new NTDMAADDParam();
            ntdmaaddParam.setAccnbr("769900019310827");
            ntdmaaddParam.setDmanbr("10101");
            ntdmaaddParam.setDmanam("测试嘿嘿");

            List<NTDMAADDParam> ntdmaaddx = new ArrayList<>();
            ntdmaaddx.add(ntdmaaddParam);

            //拼接银行请求body里面的参数
            Map<String, Object> body = new HashMap<>();
            body.put("ntdmaaddx", ntdmaaddx);

            //接口funcode参数
            String funcode = "NTDMAADD";

            List<String> requestEmptyFiledList = Arrays.asList("ovrctl", "bcktyp", "clstyp");
            JSONObject jsonObject4 = commonRequestMethod(bankConfigDto, funcode, body, requestEmptyFiledList, "参数【body】");
            System.out.println("jsonObject4 " + jsonObject4);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 银行接口公共请求方法
     * @param bankConfigDto  银行相关配置
     * @param funcode  银行接口的funcode
     * @param body  银行接口的body属性值,目前只有Object和Map<String, Object>两种类型
     * @param emptyFiledList  body属性中可以为空的属性值,没有传null
     * @param message  在为空的基础上,前面添加的提示信息,没有传null
     * @return
     * @throws Exception
     */
    public static <T> JSONObject commonRequestMethod(BankConfigDto bankConfigDto, String funcode, T body, List<String> emptyFiledList, String message) throws Exception {

        //校验body参数
        checkBody(body, emptyFiledList, message);

        return DoProcessMyself(bankConfigDto, funcode, body);
    }

    //15位真实账号+10位记账子单元编号(长度可小于10位)=25位交易识别账号,可用于对外收款,并实现自动记账功能。(交易识别不支持外币)。
    public static void modifySubAcc( SubAccUpdateDto updateDto) throws Exception {
        init();
        JSONObject jsonObject = new JSONObject();
        CMBRequest<SubAccUpdateDto> request =  new CMBRestBuilder().of(updateDto).build(CMBTransAction.AccMgt_UPDATE, UID);
        jsonObject.put("request", request);
        DoProcess(JSONObject.parseObject(jsonObject.toJSONString()), privateKey);
    }

    /**
     * 获取主账号15位
     * @param accno25
     * @return
     */
    public static String getMainAcc2Accnbr(String accno25){
        return accno25.substring(0,15);
    }

    public static String getYurrefNoByReceiptName(String receiptName){

        return receiptName.substring(receiptName.lastIndexOf("_")+1,receiptName.lastIndexOf("."));

    }

    //etytim 的格式是一个把是时分秒连起来的格式 但是数据库要求有时分秒,所以这里做一下转化
    public static String getDateStr(String dateStr){
        return dateStr.substring(0,4)+"-"+dateStr.substring(4,6)+"-"+dateStr.substring(6,8);
    }


    public  static String getEtytime(String etytim){
        return etytim.substring(0,2)+":"+etytim.substring(2,4)+":"+etytim.substring(4,6);
    }

    /**
     * 获取账号编号主账号后半部分数据10位后的数据
     * @param accno25
     * @return
     */
    public static String getSubAccUtil2Manbr(String accno25){
        return accno25.substring(15);
    }

    /**
     * 判断银行调用结果是否成功
     * @param responseJson 银行响应结果json
     * @return
     */
    public static Boolean isSuccess(JSONObject responseJson) {
        return "SUC0000".equals(getCmbResponseParam(responseJson, "head", "resultcode"));
    }

    /**
     * 获取调用银行接口的响应参数值,不能通过该方法获取signature参数下的值
     * @param responseJson 银行响应结果json
     * @param param1 要获取的结果参数名 (一级或者二级)
     * @param param2 要获取的结果参数名 (三级,没有可以为空,如果是body下的参数则一定要写正确)
     * @return
     */
    public static String getCmbResponseParam(JSONObject responseJson, String param1, String param2) {

        JSONObject response = (JSONObject) responseJson.get("response");
        checkParam(response, "返回结果中【response】为空或传入param1有误!");

        JSONObject head = (JSONObject) response.get("head");
        checkParam(head, "返回结果中【head】为空或传入param1有误!");

        JSONObject body = (JSONObject) response.get("body");
        checkParam(body, "返回结果中【body】为空或传入param1有误!");

        //head下的参数
        List<String> param2List = Arrays.asList("bizcode", "funcode", "reqid", "resultcode", "resultmsg", "rspid", "userid");

        JSONObject result = null;
        if ("response".equals(param1)) {
            result = response;
        } else if ("head".equals(param1)) {
            result = StringUtils.isNotBlank(param2) && param2List.contains(param2) ? (JSONObject) head.get(param2) : head;
        } else if ("body".equals(param1)) {
            result = StringUtils.isNotBlank(param2) && param2List.contains(param2) ? (JSONObject) body.get(param2) : body;
        } else {
            throw new RuntimeException("传入param2时param1不能为空!");
        }

        return result.toString();
    }

    /**
     * 获取请求参数json
     * @param bankConfigDto  银行接口参数,这里用来校验是否为空,这个方法只使用到了UID
     * @param funcode 接口code
     * @param body  请求json中的参数body,泛型类型
     * @return
     */
    public static <T> JSONObject getRequestJson(BankConfigDto bankConfigDto, String funcode, T body) {
        checkBankConfig(bankConfigDto);

        checkParam(funcode, "funcode不能为空!");

        String data = "{\"request\":{\"body\":" + JSON.toJSONString(body) + ",\"head\":{\"funcode\":\"" + funcode + "\",\"reqid\":\"" + getTimestamp() + "\",\"userid\":\"" + bankConfigDto.getUID() + "\"}}}";

        return JSONObject.parseObject(data);
    }

    /**
     * 校验银行body参数
     * @param body  body参数
     * @param emptyFiledList  可以为空的属性集合,为JavaBean的属性,没有可以为空的传null
     * @param message  在为空的基础上,前面添加的提示信息,没有传null
     */
    public static <T> void checkBody(T body, List<String> emptyFiledList, String message) {
        if (body instanceof Map) {
            Map<String, Object> map = (Map<String, Object>) body;
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                String key = entry.getKey();
                Object value = entry.getValue();

                List list = null;
                if (value instanceof List) {
                    list = (List) value;
                } else {
                    throw new RuntimeException("map集合中key为【" + key + "】的value值非法!应为List!");
                }

                checkList(list, emptyFiledList, message);
            }
        } else {
            checkBean(body, emptyFiledList, message);
        }
    }

    /**
     * 校验参数集合
     * @param list  参数集合
     * @param emptyFiledList  可以为空的属性集合,为JavaBean的属性,没有可以为空的传null
     * @param message  在为空的基础上,前面添加的提示信息,没有传null
     */
    public static <T> void checkList(List<T> list, List<String> emptyFiledList, String message) {
        checkParam(list, (StringUtils.isBlank(message) ? "集合" : message) + "为空!");

        for (int i = 0; i < list.size(); i++) {
            T t = list.get(i);

            checkBean(t, emptyFiledList, (StringUtils.isBlank(message) ? "集合的" : message + "的") + "第【" + i + "】条记录");
        }
    }

    /**
     * 校验参数集合
     * @param list  参数集合
     */
    public static <T> void checkList(List<T> list) {
        checkList(list, null, null);
    }

    /**
     * 校验参数集合
     * @param list  参数集合
     * @param emptyFiledList  可以为空的属性集合,没有传null
     */
    public static <T> void checkList(List<T> list, List<String> emptyFiledList) {
        checkList(list, emptyFiledList, null);
    }

    /**
     * 校验参数集合
     * @param list  参数集合
     * @param message  在为空的基础上,前面添加的提示信息,没有传null
     */
    public static <T> void checkList(List<T> list, String message) {
        checkList(list, null, message);
    }

    /**
     * 校验javabean,传其他类型的对象可能会报错
     * @param obj  校验的javabean
     * @param emptyFiledList 可以为空的属性集合,没有传null
     * @param message  在为空的基础上,前面添加的提示信息,没有传null
     */
    public static void checkBean(Object obj, List<String> emptyFiledList, String message) {
        checkParam(obj, (StringUtils.isBlank(message) ? "对象" : message) + "为空!");

        //传入为空属性集合为null表示当前集合所有属性都不能为空,但为了做校验,emptyFiledList也不能为null
        //在这里做校验可以减少http请求
        emptyFiledList = null != emptyFiledList ? emptyFiledList : new ArrayList<>();

        List<Map> objectInfoList = ReflectHelper.getFiledsInfo(obj);

        for (Map objectInfo : objectInfoList) {
            String name = (String) objectInfo.get("name"); //对象属性名,属性名都是字符串

            //可以为空的属性不做校验
            if (!emptyFiledList.contains(name)) {

                //校验对象属性值是否为空
                checkParam(objectInfo.get("value"), (StringUtils.isBlank(message) ? "对象的" : message + "的") + "【" + name + "】属性为空!");
            }
        }
    }

    /**
     * 校验javabean,传其他类型的对象可能会报错
     * @param obj  校验的javabean
     */
    public static void checkBean(Object obj) {
        checkBean(obj, null, null);
    }

    /**
     * 校验javabean,传其他类型的对象可能会报错
     * @param obj  校验的javabean
     * @param emptyFiledList  可以为空的属性集合
     */
    public static void checkBean(Object obj, List<String> emptyFiledList) {
        checkBean(obj, emptyFiledList, null);
    }

    /**
     * 校验javabean,传其他类型的对象可能会报错
     * @param obj  校验的javabean
     * @param message  在为空的基础上,前面添加的提示信息
     */
    public static void checkBean(Object obj, String message) {
        checkBean(obj, null, message);
    }

    /**
     * 校验参数是否为空
     * @param obj 参数
     * @param message 提示信息
     */
    public static void checkParam(Object obj, String message) {
        if (obj instanceof String) {
            if (StringUtils.isBlank((String) obj)) throw new RuntimeException(message);
        }
        Optional.ofNullable(obj).orElseThrow(() -> new RuntimeException(message));
    }

    /**
     * 校验银行配置是否为空
     * @param bankConfigDto 银行配置
     */
    public static void checkBankConfig(BankConfigDto bankConfigDto) {
        checkParam(bankConfigDto, "传入银行配置为空!");

        checkParam(bankConfigDto.getPubkeyStr(), "银行公钥为空!");
        checkParam(bankConfigDto.getPrikeyStr(), "银行私钥为空!");
        checkParam(bankConfigDto.getAESKEY(), "银行AESKEY为空!");
        checkParam(bankConfigDto.getAESKEY(), "银行UID为空!");
        checkParam(bankConfigDto.getURL(), "银行接口路径为空!");

//        Optional.ofNullable(bankConfigDto.getPayacc()).orElseThrow(() -> new RuntimeException("银行付方账号为空!"));
//        Optional.ofNullable(bankConfigDto.getBusmod()).orElseThrow(() -> new RuntimeException("银行模式编号为空!"));
//        Optional.ofNullable(bankConfigDto.getGrtflg()).orElseThrow(() -> new RuntimeException("银行网银审批标志为空!"));
//        Optional.ofNullable(bankConfigDto.getBbknbr()).orElseThrow(() -> new RuntimeException("银行分行号为空!"));
    }

    //这是用来获取时间戳的方法,用来生成唯一的银行接口参数reqid
    public static String getTimestamp() {
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmssSSS");//设置日期格式
        String date = df.format(new Date());// new Date()为获取当前系统时间,也可使用当前时间戳
        Random random = new Random();
        int rannum = (int) (random.nextDouble() * (99999 - 10000 + 1)) + 10000;
        // System.out.println(rannum);
        String rand = date + rannum;
        System.out.println(rand);

//        SimpleDateFormat df2 = new SimpleDateFormat("yyyyMMddHHmmss");//设置日期格式
//        String date2 = df2.format(new Date());// new Date()为获取当前系统时间,也可使用当前时间戳
//        System.out.println(date2);

        return rand;
    }








    //============================这里开始是银行的相关类

    public static void init() throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        publicKey = getPublicKeyFromBytes(pubkeyStr);
        privateKey = getPrivateKeyFromBytes(prikeyStr, "PKCS");
    }

    //这是经过自己加工的DoProcess方法
    public static <T> JSONObject DoProcessMyself(BankConfigDto bankConfigDto, String funcode, T body) throws Exception {

        //这个方法放在前面是为了校验参数
        JSONObject jObject = getRequestJson(bankConfigDto, funcode, body);

        //要先执行初始化的方法,不然调用银行接口会报错
        init();

        JSONObject object = new JSONObject();
        // 签名
        object.put("sigdat", "__signature_sigdat__");
        object.put("sigtim", GetTime());
        //object.put("sigtim", "20191023165900");
        jObject.put("signature", object);
        String source = serialJsonOrdered(jObject);
        System.out.println(source);
        String data = signRsa2048(source.getBytes(), getPrivateKeyFromBytes(bankConfigDto.getPrikeyStr(), "PKCS"));
        object.put("sigdat", data);
        jObject.put("signature", object);

        // AES加密
        //因为在前面校验过了,这里直接使用
        byte[] AESBytes = bankConfigDto.getAESKEY().getBytes();
        String AesPlainxt = serialJsonOrdered(jObject);
        System.out.println("加密前req:  " + AesPlainxt);
        String req = encryptAES256Str(AesPlainxt, AESBytes);
        System.out.println("加密后req:  " + req);

        //发送请求
        HashMap<String, String> map = new HashMap<>();
        map.put("UID", bankConfigDto.getUID());
        map.put("FUNCODE", funcode);  //银行最近新增的
        map.put("DATA", URLEncoder.encode(req, "utf-8"));
        String res = doPostForm(bankConfigDto.getURL(), map);
        System.out.println("请求结果 res:  " + res);

        //返回结果中包含这个两个字符,可能是没有白名单,也可能用户没有权限,或者其他错误
        if ((res.contains("CDCServer:") && res.contains("ErrMsg:"))) {
            throw new RuntimeException(res);
        }

        //解密请求
        String resplain = decryptAES256(res, AESBytes, true);
        System.out.println("res decrypt: " + resplain);
        JSONObject object2 =  JSONObject.parseObject(resplain);
        JSONObject object3 = object2.getJSONObject("signature");
        String resSign = object3.getString("sigdat");
        object3.put("sigdat", "__signature_sigdat__");
        object2.put("signature", object3);
        String resSignSource = serialJsonOrdered(object2);
        System.out.println("验签原文: " + resSignSource);
        System.out.println("验签签名值: " +resSign);
        Boolean verify = signRsa2048Verify(resSignSource.getBytes(), CMBBase64.decode(resSign), getPublicKeyFromBytes(bankConfigDto.getPubkeyStr()));
        System.out.println("验签结果: " + verify);
        return JSONObject.parseObject(resSignSource);
    }

    //这是原始的DoProcess
    public static String DoProcess(JSONObject jObject, PrivateKey prikey) throws Exception {
        JSONObject object = new JSONObject();
        // 签名
        object.put("sigdat", "__signature_sigdat__");
        object.put("sigtim", GetTime());
        //object.put("sigtim", "20191023165900");
        jObject.put("signature", object);
        String source = serialJsonOrdered(jObject);
        System.out.println(source);
        String data = signRsa2048(source.getBytes());
        object.put("sigdat", data);
        jObject.put("signature", object);

        // AES加密
        String AesPlainxt = serialJsonOrdered(jObject);
        System.out.println("加密前req:  " + AesPlainxt);
        String req = encryptAES256Str(AesPlainxt, AESKEY.getBytes());
        System.out.println("加密后req:  " + req);

        //发送请求
        HashMap<String, String> map = new HashMap<>();
        map.put("UID", UID);
        map.put("DATA", URLEncoder.encode(req, "utf-8"));
        String res = doPostForm(URL, map);
        System.out.println("发送请求 res:  " + res);

        //解密请求
        String resplain = decryptAES256(res, AESKEY.getBytes(), true);
        System.out.println("res decrypt: " + resplain);
        JSONObject object2 =  JSONObject.parseObject(resplain);
        JSONObject object3 = object2.getJSONObject("signature");
        String resSign = object3.getString("sigdat");
        object3.put("sigdat", "__signature_sigdat__");
        object2.put("signature", object3);
        String resSignSource = serialJsonOrdered(object2);
        System.out.println("验签原文: " + resSignSource);
        System.out.println("验签签名值: " +resSign);
        Boolean verify = signRsa2048Verify(resSignSource.getBytes(), CMBBase64.decode(resSign), publicKey);
        System.out.println("验签结果: " + verify);
        return resSignSource;
    }

    public static String GetTime() {
        Date date = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
        return dateFormat.format(date);
    }

    public static String GetTime(String datePattern) {
        Date date = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat(datePattern);
        return dateFormat.format(date);
    }

    public static String encryptAES256Str(String content, byte[] bytePassword) {
        return CMBBase64.encode(encryptAES256(content, bytePassword));
    }

    public static byte[] encryptAES256(String content, byte[] bytePassword) {
        try {
            Cipher cipherInstance = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
            SecretKeySpec key = new SecretKeySpec(bytePassword, "AES");
            cipherInstance.init(Cipher.ENCRYPT_MODE, key);
            byte[] byteContent = content.getBytes();
            byte[] cryptograph = cipherInstance.doFinal(byteContent);
            return cryptograph;
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(e.getMessage());
        }
        return bytePassword;
    }

    public static String decryptAES256(String content, byte[] bytePassword, boolean logError) {
        if (content == null || content.length() == 0) {
            System.out.println("decryptAES256 param content is null or empty");
        }
        byte[] bContent = null;
        try {
            bContent = CMBBase64.decode(content);
        } catch (Exception e) {
            System.out.println("decryptAES256  appear error");
            e.printStackTrace();
        }
        try {
            Cipher cipherInstance = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
            SecretKeySpec key = new SecretKeySpec(bytePassword, "AES");
            cipherInstance.init(Cipher.DECRYPT_MODE, key);
            byte[] crypted = cipherInstance.doFinal(bContent);
            return new String(crypted, "utf-8");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return content;
    }

    public static String signRsa2048(byte[] baSource) throws Exception {
        try {
            Signature signature = Signature.getInstance("SHA256WithRSA");
            signature.initSign(privateKey);
            signature.update(baSource);
            return CMBBase64.encode(signature.sign());
        } catch (Exception e) {
            System.out.println("signRsa2048 appear error " + e.getMessage());
            throw new Exception("signRsa2048 appear error " + e.getMessage());
        }
    }

    public static String signRsa2048(byte[] baSource, PrivateKey prvKey) throws Exception {
        try {
            Signature signature = Signature.getInstance("SHA256WithRSA");
            signature.initSign(prvKey);
            signature.update(baSource);
            return CMBBase64.encode(signature.sign());
        } catch (Exception e) {
            System.out.println("signRsa2048 appear error" + e.getMessage());
            throw new Exception("signRsa2048 appear error " + e.getMessage());
        }
    }

    public static boolean signRsa2048Verify(byte[] baSource, byte[] baSignature, PublicKey pubKey) throws Exception {
        try {
            Signature signature = Signature.getInstance("SHA256WithRSA");
            signature.initVerify(pubKey);
            signature.update(baSource);
            return signature.verify(baSignature);
        } catch (Exception e) {
            System.out.println("验签失败 " + e.getMessage());
            throw new Exception("验签失败 " + e.getMessage());
        }
    }


    public static PrivateKey getPrivateKeyFromBytes(String crtBase64, String type) throws Exception {
        // type = PKCS,X509
        try {
            byte[] baKey = CMBBase64.decode(crtBase64);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PrivateKey prvkey = keyFactory.generatePrivate(type.equals("PKCS") ? new PKCS8EncodedKeySpec(baKey) : new X509EncodedKeySpec(baKey));
            return prvkey;
        } catch (Exception e) {
            throw new Exception("getPrivateKeyFromBytes error" + e.getMessage());
        }
    }


    public static PublicKey getPublicKeyFromBytes(String crtBase64) throws Exception {
        try {
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(CMBBase64.decode(crtBase64));
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PublicKey key = keyFactory.generatePublic(keySpec);
            return key;
        } catch (Exception e) {
            throw new Exception("getPublicKeyFromBytes error" + e.getMessage());
        }
    }


    public static String doPostForm(String httpUrl, Map param) {

        HttpURLConnection connection = null;
        InputStream is = null;
        OutputStream os = null;
        BufferedReader br = null;
        String result = null;
        try {
            URL url = new URL(httpUrl);
            // trustAllHttpsCertificates();

            SSLContext sslcontext;
            sslcontext = SSLContext.getInstance("SSL", "SunJSSE");
            sslcontext.init(null, new TrustManager[] { new MyX509TrustManager() }, new java.security.SecureRandom());
            // URL url = new URL("https://xxxx");
            HostnameVerifier ignoreHostnameVerifier = new HostnameVerifier() {
                public boolean verify(String s, SSLSession sslsession) {
                    System.out.println("WARNING: Hostname is not matched for cert.");
                    return true;
                }
            };
            HttpsURLConnection.setDefaultHostnameVerifier(ignoreHostnameVerifier);
            HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext.getSocketFactory());


            connection = (HttpURLConnection) url.openConnection();

            connection.setRequestMethod("POST");

            connection.setConnectTimeout(15000);

            connection.setReadTimeout(60000);
            connection.setInstanceFollowRedirects(true);

            connection.setDoOutput(true);

            connection.setDoInput(true);


            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

            os = connection.getOutputStream();

            os.write(createLinkString(param).getBytes());

            if (connection.getResponseCode() != 200) {
                is = connection.getErrorStream();

                br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
                StringBuffer sbf = new StringBuffer();
                String temp = null;

                while ((temp = br.readLine()) != null) {
                    sbf.append(temp);
                    sbf.append("\r\n");
                }
                result = sbf.toString();
            } else {
                is = connection.getInputStream();

                br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
                StringBuffer sbf = new StringBuffer();
                String temp = null;

                while ((temp = br.readLine()) != null) {
                    sbf.append(temp);
                    sbf.append("\r\n");
                }
                result = sbf.toString();
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        } finally {

            if (null != br) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != os) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != is) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            connection.disconnect();
        }
        return result;
    }

    public static String createLinkString(Map<String, String> params) throws UnsupportedEncodingException {

        ArrayList<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);

        StringBuilder prestr = new StringBuilder();
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = params.get(key);
            if (i == keys.size() - 1) {
                prestr.append(key).append("=").append(value);
            } else {
                prestr.append(key).append("=").append(value).append("&");
            }
        }

        return prestr.toString();
    }


    public static String serialJsonOrdered(JSONObject json) {
        StringBuilder appender = new StringBuilder();
        appender.append("{");
        Iterator<String> keys = new TreeSet<>(json.keySet()).iterator();
        boolean isFirstEle = true;
        while (keys.hasNext()) {
            if (!isFirstEle) {
                appender.append(",");
            }
            String key = keys.next();
            Object val = json.get(key);
            if (val instanceof JSONObject) {
                appender.append("\"").append(key).append("\":");
                appender.append(serialJsonOrdered((JSONObject) val));
            } else if (val instanceof JSONArray) {
                JSONArray jarray = (JSONArray) val;
                appender.append("\"").append(key).append("\":[");
                boolean isFirstArrEle = true;
                for (int i = 0; i < jarray.size(); i++) {
                    if (!isFirstArrEle) {
                        appender.append(",");
                    }
                    Object obj = jarray.get(i);
                    if (obj instanceof JSONObject) {
                        appender.append(serialJsonOrdered((JSONObject) obj));
                    } else {
                        appender.append(obj.toString().replaceAll("\"", "\\\\\""));
                    }
                    isFirstArrEle = false;
                }
                appender.append("]");
            } else {
                String value = "";
                if (val instanceof String) {
                    value = "\"" + val.toString().replaceAll("\"", "\\\\\"") + "\"";
                } else {
                    value = val.toString().replaceAll("\"", "\\\\\"");
                }
                appender.append("\"").append(key).append("\":").append(value);
            }
            isFirstEle = false;
        }
        appender.append("}");
        return appender.toString();
    }
}

class MyX509TrustManager implements X509TrustManager {

    @Override
    public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
        // TODO Auto-generated method stub

    }

    @Override
    public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
        // TODO Auto-generated method stub

    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        // TODO Auto-generated method stub
        return null;
    }
}

4、工具类CMBBase64

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Base64;
import java.util.Map;

public class CMBBase64 {

    static {
        String errorString = "Failed manually overriding key-length permissions.";
        int newMaxKeyLength;
        try {
            if ((newMaxKeyLength = Cipher.getMaxAllowedKeyLength("AES")) < 256) {
                Class c = Class.forName("javax.crypto.CryptoAllPermissionCollection");
                Constructor con = c.getDeclaredConstructor();
                con.setAccessible(true);
                Object allPermissionCollection = con.newInstance();
                Field f = c.getDeclaredField("all_allowed");
                f.setAccessible(true);
                f.setBoolean(allPermissionCollection, true);

                c = Class.forName("javax.crypto.CryptoPermissions");
                con = c.getDeclaredConstructor();
                con.setAccessible(true);
                Object allPermissions = con.newInstance();
                f = c.getDeclaredField("perms");
                f.setAccessible(true);
                ((Map) f.get(allPermissions)).put("*", allPermissionCollection);

                c = Class.forName("javax.crypto.JceSecurityManager");
                f = c.getDeclaredField("defaultPolicy");
                f.setAccessible(true);
                Field mf = Field.class.getDeclaredField("modifiers");
                mf.setAccessible(true);
                mf.setInt(f, f.getModifiers() & ~Modifier.FINAL);
                f.set(null, allPermissions);

                newMaxKeyLength = Cipher.getMaxAllowedKeyLength("AES");
            }
        } catch (Exception e) {
            throw new RuntimeException(errorString, e);
        }
        if (newMaxKeyLength < 256)
            throw new RuntimeException(errorString); // hack failed
    }

    private CMBBase64() {
    }
    public static String encode(byte data[]) {
        StringBuilder sb = new StringBuilder();
        int len = data.length;
        for (int i = 0; i < len;) {
            int b1 = data[i++] & 255;
            if (i == len) {
                sb.append(ENCODE_CHARS[b1 >>> 2]);
                sb.append(ENCODE_CHARS[(b1 & 3) << 4]);
                sb.append("==");
                break;
            }
            int b2 = data[i++] & 255;
            if (i == len) {
                sb.append(ENCODE_CHARS[b1 >>> 2]);
                sb.append(ENCODE_CHARS[(b1 & 3) << 4 | (b2 & 240) >>> 4]);
                sb.append(ENCODE_CHARS[(b2 & 15) << 2]);
                sb.append("=");
                break;
            }
            int b3 = data[i++] & 255;
            sb.append(ENCODE_CHARS[b1 >>> 2]);
            sb.append(ENCODE_CHARS[(b1 & 3) << 4 | (b2 & 240) >>> 4]);
            sb.append(ENCODE_CHARS[(b2 & 15) << 2 | (b3 & 192) >>> 6]);
            sb.append(ENCODE_CHARS[b3 & 63]);
        }

        return sb.toString();
    }

    public static byte[] decode(String str) {
        byte data[] = str.getBytes();
        int len = data.length;
        ByteArrayOutputStream buf = new ByteArrayOutputStream(len);
        for (int i = 0; i < len;) {
            int b1;
            do
                b1 = DECODE_CHARS[data[i++]];
            while (i < len && b1 == -1);
            if (b1 == -1)
                break;
            int b2;
            do
                b2 = DECODE_CHARS[data[i++]];
            while (i < len && b2 == -1);
            if (b2 == -1)
                break;
            buf.write(b1 << 2 | (b2 & 48) >>> 4);
            int b3;
            do {
                b3 = data[i++];
                if (b3 == 61)
                    return buf.toByteArray();
                b3 = DECODE_CHARS[b3];
            } while (i < len && b3 == -1);
            if (b3 == -1)
                break;
            buf.write((b2 & 15) << 4 | (b3 & 60) >>> 2);
            int b4;
            do {
                b4 = data[i++];
                if (b4 == 61)
                    return buf.toByteArray();
                b4 = DECODE_CHARS[b4];
            } while (i < len && b4 == -1);
            if (b4 == -1)
                break;
            buf.write((b3 & 3) << 6 | b4);
        }

        return buf.toByteArray();
    }

    private static final int INT2 = 2;
    private static final int INT4 = 4;
    private static final int INT6 = 6;
    private static final int INT61 = 61;
    private static final char ENCODE_CHARS[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
            'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' };
    private static final byte DECODE_CHARS[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 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, -1, -1, -1, -1, -1, -1, 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, -1, -1, -1, -1, -1 };

    public static String baseConvertStr(String str) {
        if(null != str){
            Base64.Decoder decoder = Base64.getDecoder();
            try {
                return new String(decoder.decode(str.getBytes()), "GBK");
            } catch (UnsupportedEncodingException e) {
                return null;
            }
        }
        return null;
    }
}
  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
Java 调用第三方接口通常会使用工具类 RestTemplate。RestTemplate 是 Spring 框架提供的一个用于进行 HTTP 请求的工具类。它封装了 HTTP 请求的细节,提供了简单的 API 来发送 GET、POST、PUT 和 DELETE 请求,并能够方便地处理返回的响应结果。 使用 RestTemplate 需要先导入相关的依赖包,然后在代码中创建 RestTemplate 的实例。可以通过 RestTemplate 的各种方法来构建不同类型的 HTTP 请求,比如 getForObject、postForObject 等。在方法的参数中需要指定目标接口的 URL 地址,以及可能需要传递的请求参数。 在调用三方接口时,有时需要进行身份验证,可以在请求头中添加认证信息。RestTemplate 的方法中可以通过 HttpHeaders 对象来设置请求头的相关信息。另外,还可以设置请求体的内容格式,比如 JSON、XML 等。 调用接口后,可以通过 RestTemplate 的方法获取响应结果。比如,getForObject 和 postForObject 方法会返回代表响应结果的对象(可以是字符串、对象等)。另外,还可以通过 getForEntity 和 postForEntity 方法获取完整的响应对象,包括响应头、响应体等信息。 在使用 RestTemplate 调用接口时需要注意异常处理。如果调用接口出现异常,可以通过捕获 RestTemplateException 异常来处理。另外,还可以通过设置错误处理器来处理不同类型的错误响应。 总之,RestTemplate 是一个非常实用的工具类,可以方便地进行 Java 对第三方接口调用。通过它,我们可以发送 HTTP 请求,处理响应结果,并且简化了请求的编写和异常处理的过程。使用 RestTemplate 可以提高开发效率,并且可以与不同的第三方接口进行灵活的集成。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值