Java开发SDK详解

news2024/12/22 22:40:35

一、服务端开发

1、前言

1)最近在对接外部平台的接口,对方提供了一个sdk,开发中直接引入到项目中。自己只需要组装参数,直接调用sdk中的方法就可以实现接口对接。
2)sdk中包含了参数校验,加密验签,Http接口调用,还提供了各个接口入参和出参的实体类。 这些为我们减少很多的开发量,同时将一些不合规的入参直接在客户端过滤掉,减少资源浪费。还提供了一套完整的加密验签方法,避免了双发对接中因为加解密验签方法的不同导致的一些问题。很大程度上提高了我们的对接中开发联调的成本。
3)sdk优点这么多,下面我们来开发一个简单的sdk吧。

2、步骤概述

1)首先我们开发一下服务端: 服务端是一个SpringBoot项目提供了一个用户列表查询的接口。入参包含调用方分唯一编号(appId),加密的入参信息(data),入参的加解密方式使用RSA非对称加密算法。

2)其次开发SDK: SDK是一个普通的maven项目,提供用户列表查询接口的Http调用方法,还有接口的参数校验,入参的加密,返回信息的解密。最后将项目打成jar包。

3)最后开发客户端调试: 客户端是一个SpringBoot项目,项目中引入上面打包好的SDK,组装参数,调用用户列表查询接口。

3、服务端开发

3.1代码实现

3.1.1 服务端入口(SysUserApiController)

这个类是接收客户端请求的入口,继承了ApiAbstract

package com.zkaw.api;

/**
 * @Author: best_liu
 * @Description:
 * @Date Create in 10:00 2023/5/19
 * @Modified By:
 */

import com.alibaba.fastjson.JSONObject;
import com.zkaw.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/user/")
public class SysUserApiController extends ApiAbstract<String> {

    @Autowired
    private IUserService sysUserService;

    @Override
    public String work(String type,JSONObject o) {
        return sysUserService.invokeApi(type,JSONObject.toJavaObject(o, SysUserDTO.class));
    }

    @PostMapping("/invokeApi")
    public HopeResponse invokeApi(@RequestBody HopeRequest hopeRequest) {
        return process(hopeRequest);
    }

}

3.1.2 接口的抽象类(ApiAbstract)

这个类提供一些公用的方法,私钥的获取,入参的解密,出参的加密等。

 

package com.zkaw.api;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.Map;

/**
 * @Author: best_liu
 * @Description:
 * @Date Create in 10:04 2023/5/19
 * @Modified By:
 */


@Slf4j
public abstract class ApiAbstract<R> {

    private static final Map<String, String> PRIVATE_KEY_MAP = new HashMap<>();

    static {
        PRIVATE_KEY_MAP.put("zkawTest", "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALCvyp0epQHwTaU+WPx+Zue2iVR3JcheHZTUEITPHW9anhW8iMkteE3m83Zu2e6V1/LbnaVoNG3GO3i65C6MiBMeJha1PKgYrlvHe5cLzWlWwQ6BxCOCto2m5sLWXd24xJp259qlKypw1F9dMVqJAaCO+2sDTUBbdBL9yJLB6J51AgMBAAECgYBMZseTZ3Pswa+hm7M5A6OV4N3Dc34YBXJ/L7Aw7aqJw0KGna78ZzgVX5+5f7enLo5hysuBgezh5w1CaHZRFp2kBqsYn7z3oGo2Mbs8H6fF1M4r3pRwtt70FgNuTGDm7KLkzHyFHxBmTl15AxujNTTQBxai/rZkUlfVCbHS3+PJpQJBANsY2MxiRLxM2CcWy+AuKLGNx+sT7Z3WfJOKQ09Jay55fsMrIhCKuE/yf4pGVaQuNMsNsRhpiZEE4nbAoZXGtiMCQQDOckUMKy4rEY2i2yI1g8JcaI7lGKA+Ey22Sy2azFSSPpfIW5ZkgCpzKUx241Z6CCNmIw8nhM7SfaYTUoR0+0aHAkEAv/DmjTdRzDzt6GC6Py1xDQuOps0QkARFouOv0Bgbw91ARi7VavR8P93MChFQGcM5EOJv0Vkz4U4ML8jWRmaNTQJAbNaTjmnJak1TxZMPwvTW6A77ns5P1MoZpGyX+29T+tHjBW87p3pmZtZUCK2r7qQRvYwoNiZvP0uc3bz4NhAvSwJBAKjsvDaf/JXdS9wrfHTT/d7DuBOf3WVkiex8pVeStBcd65qHzF+faWw5VJb3Y6IMgtoSt1QMgMuEjlst4bcL6ss=");
    }

    /**
     * 统一处理
     *
     * @param hopeRequest 统一请求
     * @return 统一响应
     */
    public HopeResponse process(HopeRequest hopeRequest) {
        try {
            String privateKey = getPrivateKey(hopeRequest.getAppId());
            String type = hopeRequest.getType();
            JSONObject o = before(hopeRequest.getData(), privateKey);
            System.out.println(o);
            R r = work(type,o);
            return after(r, privateKey);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("ServerAbstract process exception! hopeRequest={}", hopeRequest, e);
            return HopeResponse.builder()
                    .success(false)
                    .message(e.getCause().toString())
                    .build();
        }

    }

    /**
     * 处理前置解密
     *
     * @param data       请求密文
     * @param privateKey 秘钥
     * @return 请求参数
     */
    private JSONObject before(String data, String privateKey) {
        return JSON.parseObject(RsaUtil.decryptPrivate(privateKey, data));
    }

    /**
     * 业务处理
     *
     * @param o 入参
     * @return 返回
     */
    abstract R work(String type,JSONObject o);

    /**
     * 后置处理加密
     *
     * @param r          业务数据
     * @param privateKey 秘钥
     * @return 统一返回
     */
    private HopeResponse after(R r, String privateKey) {
        return HopeResponse.builder()
                .success(true)
                .message("访问成功")
                .data(RsaUtil.encryptPrivate(privateKey, (String) r))
                .build();
    }

    /**
     * 通过appId获取对应的私钥,不同的接入方提供不同的公私钥。
     * 实际业务开发中这些会存在文件中或者配置中心中如阿波罗,这里简单实现
     *
     * @param appId appId
     * @return 私钥
     */
    private String getPrivateKey(String appId) {
        return PRIVATE_KEY_MAP.get(appId);
    }
}

3.1.3 接口入参实体类(HopeRequest)

这个是接口的入参,这里简单演示,一个客户端唯一编号(用来获取对应的私钥),一个是加密的入参。

package com.zkaw.api;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @Author: best_liu
 * @Description:
 * @Date Create in 10:04 2023/5/19
 * @Modified By:
 */

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class HopeRequest {
    /**
     * 客户端唯一编号
     */
    private String appId;

    private String type;
    /**
     * 加密后业务相关的入参
     */
    private String data;
}


 

3.1.4 接口返回实体类(HopeResponse)

这个是服务端接口的返回信息。返回的字段有成功表示,提示信息,还有业务相关的查询结果(查询到的用户信息)加密的信息。

 

package com.zkaw.api;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @Author: best_liu
 * @Description:
 * @Date Create in 10:05 2023/5/19
 * @Modified By:
 */

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class HopeResponse {

    /**
     * 是否成功
     */
    private boolean success;

    /**
     * 返回信息
     */
    private String message;

    /**
     * 业务相关的返回信息,私钥加密之后的
     */
    private String data;

}

3.1.5 具体的业务实现层(SysUserService)
这里是具体的业务层,通过入参去数据库查询对应的结果,在封装成实体类返回,这里不再深入。PageMode是用查询的结果封装的分页实体类。BaseResponse是业务层处理的结果有返回码和提示信息。其实这里不用封装这么多层,我这个是用以前的老代码简单的做个示例。

package com.zkaw.service;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.zkaw.api.SysUser;
import com.zkaw.api.SysUserDTO;
import com.zkaw.pojo.User;

import java.util.List;

/**
 * @Author: LiuXingJie
 * @Description:
 * @Date Create in 11:06 2022/6/29
 * @Modified By:
 */
public interface IUserService {

    int insertSaveBanch(List<User> list);

    int insert( User user);

    int delete( User user);

    IPage<User> selectPage(User user);

    int update( User user);

    List<User> selectList(User user);

    List<SysUser> queryUserList(SysUserDTO dto);

    SysUser selectById(String userId);

    String invokeApi(String type,SysUserDTO dto);
}

3.1.6 业务相关的入参(SysUserDTO)

这个类是真正传递业务相关查询条件的实体类。PageBase 是一个分页相关的类,参数为pageSize和pageIndex。

 

package com.zkaw.api;
import lombok.Data;

/**
 * @Author: best_liu
 * @Description:
 * @Date Create in 10:05 2023/5/19
 * @Modified By:
 */

@Data
public class SysUserDTO extends PageBase {


    private int id;

    /**
     * 昵称
     */
    private String userId;
    private String age;

    /**
     * 用户名称
     */
    private String userName;

    /**
     * 用户手机号
     */
    private String userPhone;

    /**
     * 用户邮箱
     */
    private String userEmail;

    /**
     * 用户状态 0、正常 1、锁定 2、注销
     */
    private Integer status;
}

3.1.7 RSA加解密的工具类(RsaUtil)

package com.zkaw.api;

import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;

/**
 * @Author: best_liu
 * @Description:
 * @Date Create in 10:07 2023/5/19
 * @Modified By:
 */

@Slf4j
public class RsaUtil {

    /**
     * 算法加解密算法
     */
    private static final String ALGORITHM = "RSA";

    /**
     * 最大加密字节数,超出最大字节数需要分组加密
     */
    private static final Integer MAX_ENCRYPT_BLOCK = 117;

    /**
     * 最大解密字节数,超出最大字节数需要分组解密
     */
    private static final Integer MAX_DECRYPT_BLOCK = 128;
    /**
     * RSA密钥长度必须是64的倍数,在512~65536之间。默认是1024
     */
    public static final int KEY_SIZE = 1024;
    

    /**
     * 请求报文公钥解密
     *
     * @param publicKeyString 公钥
     * @param text            报文
     * @return 加密报文
     */
    public static String encryptPublic(String publicKeyString, String text) {
        try {
            PublicKey publicKey = getPublicKey(publicKeyString);
            return encryptRSA(publicKey, text);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("RsaUtil encryptPublic exception! publicKeyString={} text={}", publicKeyString, text);
            return null;
        }
    }

    /**
     * 应答报文公钥解密
     *
     * @param publicKeyString 公钥
     * @param text            应答密文
     * @return 解密报文
     */
    public static String decryptPublic(String publicKeyString, String text) {
        try {
            PublicKey publicKey = getPublicKey(publicKeyString);
            return decryptRSA(publicKey, text);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("RsaUtil decryptPublic exception! publicKeyString={} text={}", publicKeyString, text);
            return null;
        }
    }

    /**
     * 请求报文私钥解密
     *
     * @param privateKeyString 私钥
     * @param text             报文
     * @return 加密报文
     */
    public static String encryptPrivate(String privateKeyString, String text) {
        try {
            PrivateKey privateKey = getPrivateKey(privateKeyString);
            return encryptRSA(privateKey, text);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("RsaUtil encryptPrivate exception! publicKeyString={} text={}", privateKeyString, text);
            return null;
        }
    }

    /**
     * 应答报文私钥解密
     *
     * @param privateKeyString 私钥
     * @param text             应答密文
     * @return 解密报文
     */
    public static String decryptPrivate(String privateKeyString, String text) {
        try {
            PrivateKey privateKey = getPrivateKey(privateKeyString);
            return decryptRSA(privateKey, text);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("RsaUtil decryptPrivate exception! privateKeyString={} text={}", privateKeyString, text);
            return null;
        }
    }

    /**
     * RSA 加密
     *
     * @param key  密钥
     * @param text 原文
     * @return 密文
     * @throws Exception 异常
     */
    private static String encryptRSA(Key key, String text) throws Exception {
        // 创建加密对象
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        // 对加密进行初始化 第一个参数是加密模式,第二个参数是你想用的公钥加密还是私钥加密
        cipher.init(Cipher.ENCRYPT_MODE, key);
        // 分段加密
        byte[] make = doCrypt(text.getBytes(), cipher, MAX_ENCRYPT_BLOCK);
        return Base64.encode(make);
    }

    /**
     * RSA 解密
     *
     * @param key  密钥
     * @param text 密文
     * @return 明文
     * @throws Exception 异常
     */
    private static String decryptRSA(Key key, String text) throws Exception {
        // 创建加解密对象
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        // 对解密进行初始化 第一个参数是加密模式,第二个参数是你想用的公钥解密还是私钥解密
        cipher.init(Cipher.DECRYPT_MODE, key);
        //分段解密
        byte[] make = doCrypt(Base64.decode(text), cipher, MAX_DECRYPT_BLOCK);
        return new String(make);
    }

    /**
     * 分段加解密
     *
     * @param data     要加解密的内容数组
     * @param cipher   加解密对象
     * @param maxBlock 分段大小
     * @return 结果
     * @throws IllegalBlockSizeException 异常
     * @throws BadPaddingException       异常
     */
    private static byte[] doCrypt(byte[] data, Cipher cipher, Integer maxBlock) throws IllegalBlockSizeException, BadPaddingException {
        int inputLength = data.length;
        // 标识
        int offSet = 0;
        byte[] resultBytes = {};
        byte[] cache;
        while (inputLength - offSet > 0) {
            if (inputLength - offSet > maxBlock) {
                cache = cipher.doFinal(data, offSet, maxBlock);
                offSet += maxBlock;
            } else {
                cache = cipher.doFinal(data, offSet, inputLength - offSet);
                offSet = inputLength;
            }
            resultBytes = Arrays.copyOf(resultBytes, resultBytes.length + cache.length);
            System.arraycopy(cache, 0, resultBytes, resultBytes.length - cache.length, cache.length);
        }
        return resultBytes;
    }

    /**
     * 获取私钥
     *
     * @param privateKeyString 私钥路径
     * @return 私钥
     */
    private static PrivateKey getPrivateKey(String privateKeyString) throws Exception {
        // 创建key的工厂
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        // 创建私钥key的规则
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.decode(privateKeyString));
        // 返回私钥对象
        return keyFactory.generatePrivate(keySpec);
    }

    /**
     * 获取公钥
     *
     * @param publicKeyString 公钥
     * @return 公钥
     * @throws Exception 异常
     */
    private static PublicKey getPublicKey(String publicKeyString) throws Exception {
        // 创建key的工厂
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        // 创建公钥key的规则
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decode(publicKeyString));
        // 返回公钥对象
        return keyFactory.generatePublic(keySpec);
    }


    public static void main(String[] args) throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        //所以当密钥位数为2048时,最大解密长度应为256.
        keyPairGenerator.initialize(KEY_SIZE);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        System.out.println("publicKey="+publicKey);
        System.out.println("==============================");
        System.out.println("privateKey="+privateKey);
    }

}

二、SDK开发

1、前言

  • 前面已经将服务端开发好了(服务端开发),现在我们来开发SDK吧。

2、SDK开发

2.1 创建项目

  • 创建一个普通的maven项目 maven----》jdk选择1.8-----》next
  • 输入groupId和artifactId
  • 输入项目名称,和项目存放位置

创建项目步骤不在这里面说了,相信大家应该都已经会了。

2.2 开发代码

先看看项目的整体结构

 

 

2.2.1 pom文件

依赖的jar包

 

<dependencies>
        <!--json相关-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.32</version>
        </dependency>
        <!-- 添加slf4j日志api -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--工具类-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.4</version>
        </dependency>
    </dependencies>

2.2.2 SysUserClient

用户查询的客户端,继承ClientAbstract 类

 

package com.best.hope.client;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.best.hope.domain.SysUserDTO;
import com.best.hope.domain.common.ApiRequest;
import com.best.hope.domain.common.HopeRequest;
import lombok.extern.slf4j.Slf4j;

/**
 * @Author: best_liu
 * @Description:
 * @Date Create in 10:16 2023/5/19
 * @Modified By:
 */

@Slf4j
public class SysUserClient extends ClientAbstract {

    public static String invokeApi(HopeRequest hopeRequest) {
        ApiRequest<SysUserDTO> request = ApiRequest.<SysUserDTO>builder()
                .appId(hopeRequest.getAppId())
                .type(hopeRequest.getType())
                .url("http://localhost:8090/api/user/invokeApi")
                .publicKey(hopeRequest.getPublicKey())
                .data(JSONObject.parseObject(hopeRequest.getData(),SysUserDTO.class))
                .build();
        try {
            String str = post(request);
            return JSON.parseObject(str, new TypeReference<String>() {
            });
        } catch (Exception e) {
            log.error("SysUserClient queryUserList is exception! request={}", request);
            return null;
        }
    }
}

2.2.3 ClientAbstract

提供了入参加密,返回解密的功能,http请求。这里也可以添加参数校验的功能,这里省略。

 

package com.best.hope.client;

import com.alibaba.fastjson.JSON;

import com.alibaba.fastjson.JSONObject;
import com.best.hope.domain.common.ApiRequest;
import com.best.hope.domain.common.HopeRequest;
import com.best.hope.domain.common.HopeResponse;
import com.best.hope.enums.ReturnCodeEnum;
import com.best.hope.exception.HopeException;
import com.best.hope.utils.HttpUtil;
import com.best.hope.utils.RsaUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

/**
 * @Author: best_liu
 * @Description:
 * @Date Create in 10:16 2023/5/19
 * @Modified By:
 */

@Slf4j
class ClientAbstract {

    static String post(ApiRequest request) {
        HopeRequest hopeRequest = HopeRequest.builder()
                .appId(request.getAppId())
                .type(request.getType())
                .data(RsaUtil.encrypt(request.getPublicKey(), JSON.toJSONString(request.getData())))
                .build();
        String s = HttpUtil.doPost(request.getUrl(), JSON.toJSONString(hopeRequest));
        if (StringUtils.isBlank(s)) {
            log.error("client post api result is null!");
            throw new HopeException("400", "请求结果为null");
        }
        HopeResponse hopeResponse = JSON.parseObject(s, HopeResponse.class);
        if (!hopeResponse.isSuccess()) {
            log.error("client post api error! hopeResponse={}", hopeResponse);
            throw new HopeException("400", hopeResponse.getMessage());
        }
        hopeResponse.setData(RsaUtil.decrypt(request.getPublicKey(), hopeResponse.getData()));
        return JSONObject.toJSONString(hopeResponse);
    }
}

2.2.4 HttpUtil

Http请求的工具类,这里简单写一个psot请求的方法。参数传递方法为application/json。

 

package com.best.hope.utils;

import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * @Author: best_liu
 * @Description:
 * @Date Create in 10:17 2023/5/19
 * @Modified By:
 */

@Slf4j
public class HttpUtil {

    /**
     * Http post请求
     *
     * @param httpUrl 连接
     * @param param   参数
     * @return
     */
    public static String doPost(String httpUrl, String param) {
        log.info(" HttpUtil doPost begin! httpUrl={} param={}", httpUrl, param);
        StringBuilder result = new StringBuilder();
        //连接
        HttpURLConnection connection = null;
        OutputStream os = null;
        InputStream is = null;
        BufferedReader br = null;
        try {
            //创建连接对象
            URL url = new URL(httpUrl);
            //创建连接
            connection = (HttpURLConnection) url.openConnection();
            //设置请求方法
            connection.setRequestMethod("POST");
            //设置连接超时时间
            connection.setConnectTimeout(15000);
            //设置读取超时时间
            connection.setReadTimeout(15000);
            //DoOutput设置是否向httpUrlConnection输出,DoInput设置是否从httpUrlConnection读入,此外发送post请求必须设置这两个
            //设置是否可读取
            connection.setDoOutput(true);
            connection.setDoInput(true);
            //设置通用的请求属性
            connection.setRequestProperty("Content-Type", "application/json;charset=utf-8");
            connection.setRequestProperty("connection", "Keep-Alive");
            //拼装参数
            if (null != param && !param.equals("")) {
                //设置参数
                os = connection.getOutputStream();
                //拼装参数
                os.write(param.getBytes("UTF-8"));
            }
            //开启连接
            connection.connect();
            //读取响应
            if (connection.getResponseCode() == 200) {
                is = connection.getInputStream();
                if (null != is) {
                    br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
                    String temp = null;
                    while (null != (temp = br.readLine())) {
                        result.append(temp);
                        result.append("\r\n");
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
            log.error("HttpUtil doPost exception! httpUrl={} param={}", httpUrl, param, e);
        } finally {
            //关闭连接
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //关闭连接
            if (connection != null) {
                connection.disconnect();
            }
        }
        log.info(" HttpUtil doPost end! result={}", result);
        return result.toString();
    }

}


2.2.5 RsaUtil

RSA加解密的工具类

package com.best.hope.utils;

import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;

/**
 * @Author: best_liu
 * @Description:
 * @Date Create in 10:18 2023/5/19
 * @Modified By:
 */

@Slf4j
public class RsaUtil {


    /**
     * 算法加解密算法
     */
    private static final String ALGORITHM = "RSA";

    /**
     * 最大加密字节数,超出最大字节数需要分组加密
     */
    private static final Integer MAX_ENCRYPT_BLOCK = 117;

    private static final Integer MAX_DECRYPT_BLOCK = 128;

    /**
     * 请求报文公钥解密
     *
     * @param publicKeyString 公钥
     * @param text            报文
     * @return 加密报文
     */
    public static String encrypt(String publicKeyString, String text) {
        try {
            PublicKey publicKey = getPublicKey(publicKeyString);
            return encryptRSA(publicKey, text);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("RsaUtil encrypt exception! publicKeyString={} text={}", publicKeyString, text);
            return null;
        }
    }

    /**
     * 应答报文公钥解密
     *
     * @param publicKeyString 公钥
     * @param text            应答密文
     * @return 解密报文
     */
    public static String decrypt(String publicKeyString, String text) {
        try {
            PublicKey publicKey = getPublicKey(publicKeyString);
            return decryptRSA(publicKey, text);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("RsaUtil decrypt exception! publicKeyString={} text={}", publicKeyString, text);
            return null;
        }
    }

    /**
     * RSA 加密
     *
     * @param key  密钥
     * @param text 原文
     * @return 密文
     * @throws Exception 异常
     */
    private static String encryptRSA(Key key, String text) throws Exception {
        // 创建加密对象
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        // 对加密进行初始化 第一个参数是加密模式,第二个参数是你想用的公钥加密还是私钥加密
        cipher.init(Cipher.ENCRYPT_MODE, key);
        // 分段加密
        byte[] make = doCrypt(text.getBytes(), cipher, MAX_ENCRYPT_BLOCK);
        return Base64.encode(make);
    }

    /**
     * RSA 解密
     *
     * @param key  密钥
     * @param text 密文
     * @return 明文
     * @throws Exception 异常
     */
    private static String decryptRSA(Key key, String text) throws Exception {
        // 创建加解密对象
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        // 对解密进行初始化 第一个参数是加密模式,第二个参数是你想用的公钥解密还是私钥解密
        cipher.init(Cipher.DECRYPT_MODE, key);
        //分段解密
        byte[] make = doCrypt(Base64.decode(text), cipher, MAX_DECRYPT_BLOCK);
        return new String(make);
    }

    /**
     * 分段加解密
     *
     * @param data     要加解密的内容数组
     * @param cipher   加解密对象
     * @param maxBlock 分段大小
     * @return 结果
     * @throws IllegalBlockSizeException 异常
     * @throws BadPaddingException       异常
     */
    private static byte[] doCrypt(byte[] data, Cipher cipher, Integer maxBlock) throws IllegalBlockSizeException, BadPaddingException {
        int inputLength = data.length;
        // 标识
        int offSet = 0;
        byte[] resultBytes = {};
        byte[] cache;
        while (inputLength - offSet > 0) {
            if (inputLength - offSet > maxBlock) {
                cache = cipher.doFinal(data, offSet, maxBlock);
                offSet += maxBlock;
            } else {
                cache = cipher.doFinal(data, offSet, inputLength - offSet);
                offSet = inputLength;
            }
            resultBytes = Arrays.copyOf(resultBytes, resultBytes.length + cache.length);
            System.arraycopy(cache, 0, resultBytes, resultBytes.length - cache.length, cache.length);
        }
        return resultBytes;
    }

    /**
     * 获取私钥
     *
     * @param privateKeyString 私钥路径
     * @return 私钥
     */
    private static PrivateKey getPrivateKey(String privateKeyString) throws Exception {
        // 创建key的工厂
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        // 创建私钥key的规则
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.decode(privateKeyString));
        // 返回私钥对象
        return keyFactory.generatePrivate(keySpec);
    }

    /**
     * 获取公钥
     *
     * @param publicKeyString 公钥
     * @return 公钥
     * @throws Exception 异常
     */
    private static PublicKey getPublicKey(String publicKeyString) throws Exception {
        // 创建key的工厂
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        // 创建公钥key的规则
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decode(publicKeyString));
        // 返回公钥对象
        return keyFactory.generatePublic(keySpec);
    }
}


 

2.2.5 App

测试类

 

package com.best.hope;

/**
 * @Author: best_liu
 * @Description:
 * @Date Create in 10:18 2023/5/19
 * @Modified By:
 */

import com.alibaba.fastjson.JSONObject;
import com.best.hope.client.SysUserClient;
import com.best.hope.domain.SysUserDTO;
import com.best.hope.domain.common.HopeRequest;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class App {

    /**
     * 公钥
     */
    private static final String PUBLIC_KEY_STRING = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCwr8qdHqUB8E2lPlj8fmbntolUdyXIXh2U1BCEzx1vWp4VvIjJLXhN5vN2btnuldfy252laDRtxjt4uuQujIgTHiYWtTyoGK5bx3uXC81pVsEOgcQjgraNpubC1l3duMSadufapSsqcNRfXTFaiQGgjvtrA01AW3QS/ciSweiedQIDAQAB";

//    public static void main(String[] args) {
//        SysUserDTO dto = new SysUserDTO();
//        dto.setUserName("吴素");
//        dto.setId(9);
//        dto.setAge("20");
//        dto.setUserEmail("nangong@163.com");
//        HopeRequest hopeRequest = new HopeRequest();
//        hopeRequest.setAppId("zkawTest");
//        hopeRequest.setPublicKey(PUBLIC_KEY_STRING);
//        hopeRequest.setType("list");
//        hopeRequest.setData(JSONObject.toJSONString(dto));
//        String pageModelBaseResponse = SysUserClient.invokeApi(hopeRequest);
//        System.out.println(pageModelBaseResponse);
//    }

    /** @Author: best_liu
     * @Description:java中通过反射调用某个类中的方法
     * @Date: 9:38 2023/5/26
     * @Param [args]
     * @return void
     **/
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        SysUserDTO dto = new SysUserDTO();
//        dto.setUserName("吴素");
        dto.setId(9);
        dto.setAge("20");
        dto.setUserEmail("nangong@163.com");
        HopeRequest hopeRequest = new HopeRequest();
        hopeRequest.setAppId("zkawTest");
        hopeRequest.setPublicKey(PUBLIC_KEY_STRING);
        hopeRequest.setType("list");
        hopeRequest.setData(JSONObject.toJSONString(dto));
        Class clazz  =  SysUserClient.class;
        Method method =  clazz.getDeclaredMethod("invokeApi",new Class[]{HopeRequest.class});
        Object invoke = method.invoke(new SysUserClient(), new Object[]{hopeRequest});
        System.out.println(invoke);
    }

    /** @Author: best_liu
     * @Description: java中通过反射获取某个类中的所有方法信息
     * @Date: 14:52 2023/5/25
     * @Param [args]
     * @return void
     **/
//    public static void main(String args[]) throws Exception {
//        Class<?> cls = SysUserClient.class;
        public Method[] getMethods();//获取包括自身和继承(实现)过来的所有的public方法——Method不支持泛型<>,即后面不接<>
        public Method[] getDeclaredMethods();//获取自身所有的方法(private、public、protected,和访问权限无关),不包括继承的
//        Method methods[] = cls.getDeclaredMethods();
//        for (Method met : methods) {
//            String res = "";
//            int mod = met.getModifiers();
//            //先输出方法名字
//            res = res + Modifier.toString(mod) + " " + met.getReturnType().getName() + " " + met.getName() +  "(";
//            Class<?> params[] = met.getParameterTypes();
//            for (int x = 0; x < params.length; x ++) {
//                //拼凑出方法要求的数据类型
//                res = res + params[x].getName() + " "  + "args-" + x;
//                if (x < params.length - 1) {
//                    res = res +",";
//                }
//            }
//            res = res + ") ";
//            Class<?> exp[] = met.getExceptionTypes();
//            if (exp.length > 0) {
//                //获取其支持处理的异常信息
//                res = res + "throws ";
//            }
//            for (int x = 0 ; x < exp.length ; x ++) {
//                res = res + exp[x].getName();
//                if (x < exp.length - 1) {
//                    res = res +",";
//                }
//            }
//            System.out.println(res);
//            System.out.println(); //换行
//        }
//    }
}

2.2.6 HopeRequest

这个是接口的入参,这里简单演示,一个客户端唯一编号(用来获取对应的私钥),一个是加密的入参。

 

package com.best.hope.domain.common;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @Author: best_liu
 * @Description:
 * @Date Create in 10:20 2023/5/19
 * @Modified By:
 */

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class HopeRequest {
    /**
     * 客户端唯一编号
     */
    private String appId;

    private String publicKey;

    private String type;

    /**
     * 加密后业务相关的入参
     */
    private String data;
}

2.2.6 HopeResponse

package com.best.hope.domain.common;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @Author: best_liu
 * @Description:
 * @Date Create in 10:21 2023/5/19
 * @Modified By:
 */

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class HopeResponse {

    /**
     * 是否成功
     */
    private boolean success;

    /**
     * 返回信息
     */
    private String message;

    /**
     * 业务相关的返回信息,私钥加密之后的
     */
    private String data;

}

2.2.7 ApiRequest

这个类是创建入参是用的,有客户端唯一Id(appId),请求的接口地址,公钥还有业务相关的入参。

 

package com.best.hope.domain.common;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @Author: best_liu
 * @Description:
 * @Date Create in 10:21 2023/5/19
 * @Modified By:
 */

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ApiRequest<T> {

    private String url;
    private String type;
    private String publicKey;
    private String appId;
    private T data;

}

2.3 打包
2.3.1 设置
File ——》 Project Structure ——》 Project Settings ——》 Artifacts ——》 右栏左上角+ ——》JAR ——》 From Modules with dependencies——》OK

直接确定无需指定主类

不用更改 点击apply


2.3.2 构建
Build ——》 Build Artifacts


Build(第一次构建)
Rebuild(重新构建,会先自定清理上次构建jar包)
Clean(清理构建好的jar包)

jar生成在out文件夹下

三、maven 引入本地sdk包

3.1、cmd执行以下命令

mvn install:install-file -Dmaven.repo.local=C:\Users\lx\.m2\repository -DgroupId=com.taobao -DartifactId=taobao-sdk-java-auto -Dversion=1.0.1 -Dpackaging=jar -Dfile=C:\Users\lx\Downloads\taobao-sdk-java-auto_1479188381469-20211105.jar

参数

Dmaven.repo.local   指定的maven仓库的地址
DgroupId            指maven创库存放jar包的路径, 也是pom文件中groupId:标签中的值;
DartifactId         指是pom文件中artifactId标签中的值
Dversion            指版本号;
Dfile               指当前jar所存放的位置

3.2、在pom中引入依赖

<dependency>
            <groupId>com.best.hope</groupId>
            <artifactId>hope-sdk</artifactId>
            <version>1.0.1</version>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-api</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>ch.qos.logback</groupId>
                    <artifactId>logback-classic</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

3.3、测试

 /** @Author: best_liu
     * @Description:java中通过反射调用某个类中的方法
     * @Date: 9:38 2023/5/26
     * @Param [args]
     * @return void
     **/
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        SysUserDTO dto = new SysUserDTO();
//        dto.setUserName("吴素");
        dto.setId(9);
        dto.setAge("20");
        dto.setUserEmail("nangong@163.com");
        HopeRequest hopeRequest = new HopeRequest();
        hopeRequest.setAppId("zkawTest");
        hopeRequest.setPublicKey(PUBLIC_KEY_STRING);
        hopeRequest.setType("list");
        hopeRequest.setData(JSONObject.toJSONString(dto));
        Class clazz  =  SysUserClient.class;
        Method method =  clazz.getDeclaredMethod("invokeApi",new Class[]{HopeRequest.class});
        Object invoke = method.invoke(new SysUserClient(), new Object[]{hopeRequest});
        System.out.println(invoke);
    }

接口已经调通结果已经打印在控制台

四、最后 

一个简单的java开发sdk得demo完成~

maven 引入本地sdk包:JAVA----maven 引入本地sdk包_Best_Liu~的博客-CSDN博客

 java反射获取类方法及调用类方法:java反射获取方法以及调用方法_Best_Liu~的博客-CSDN博客

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

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

相关文章

react项目+antd组件-demo:hello-world

在前端开发过程中&#xff0c;有涉及到使用antd组件部分。在项目中加一个antd,调整组件的大小、位置、颜色&#xff0c;花费时间比较多&#xff0c;效率不高&#xff0c;可以通过本文叙述的方式建立一个前端demo&#xff0c;用于调整组件的大小、位置、颜色&#xff0c;验证组件…

C语言_结构体

文章目录 一、结构体结构的基础知识 二、结构体类型的声明三. 结构体初始化四.结构成员的类型五.结构体变量的定义和初始化六.结构体成员访问6.1结构体变量访问成员6.2结构体指针访问指向变量的成员&#xff08;箭头操作符 ->&#xff09; 七.结构体传参总结 一、结构体 结…

MySQL 事务简介

事务简介 事务的起源 狗哥和猫爷是⼀对好基友&#xff0c;他们都到银⾏开⼀个账户&#xff0c;他们在现实世界中拥有的资产就会体现在数据库世界的account表中。⽐如现在狗哥有11元&#xff0c;猫爷只有2元&#xff0c;那么现实中的这个情况映射到数据库的account表就是这样&…

机器学习常识 11: logistic 回归

摘要: logistic 回归是使用超平面将空间分开, 一边是正样本, 另一边是负样本. 因此, 它是一个线性分类器. 1. 线性分类器 如图 1 所示, 若干样本由两个特征描述, 对应于二维平面上的点. 它们为正样本或负样本, 由不同颜色表示. 现在需要使用一条直线将正、负样本分开. 这样, …

比ureport好用的报表工具-VeryReport报表工具

作为一名报表开发人员&#xff0c;你一定知道&#xff0c;一个好用的报表工具是多么重要。它可以让你更快、更准确地完成报表开发任务&#xff0c;并且帮助你更好地展现数据。今天我想向大家介绍一款非常优秀的报表工具——VeryReport报表工具。 编辑搜图 请点击输入图片描述&…

STM32 启动文件选择

1. STM32F1xx 系列 &#xff08;F1&#xff09; STM32F100xB/C: startup_stm32f100xb.s STM32F100xD/E: startup_stm32f100xe.s STM32F101x6/8/B: startup_stm32f101x6.s, startup_stm32f101x8.s, startup_stm32f101xb.s STM32F101xE/F/G: startup_stm32f101xe.s, …

线程安全与互斥锁(访问控制)

线程安全问题 因为多个线程是共享地址空间的&#xff0c;也就是很多资源都是共享的。 优点:通信方便缺点:缺乏访问控制 因为一个线程的操作问题&#xff0c;给其他线程造成了不可控&#xff0c;或者引起崩溃&#xff0c;异常&#xff0c;逻辑不正确等这种现象:线程安全。 创…

基于Java实现农产品交易平台的设计与实现_kaic

【摘要】农业是我国国民经济的重要组成部分&#xff0c;随着信息化的普及&#xff0c;4G网络、光纤以及5G网络也日益完善&#xff0c;农业信息化的发展成为了必然。同时&#xff0c;由于本年疫情原因&#xff0c;导致农作物积压销售&#xff0c;甚至腐烂造成不必要的浪费&#…

chatgpt赋能python:Python信息抽取——帮您更好地利用数据

Python信息抽取——帮您更好地利用数据 什么是Python信息抽取&#xff1f; Python信息抽取是指利用Python编写程序&#xff0c;从大量的非结构化数据中提取有用的信息的技术。这些数据可以是网页、文本文件、PDF等各种格式&#xff0c;而Python信息抽取则可以帮助您快速、准确…

分享一个case when then when then end的sql语句编写用法

目录 写作背景我和若依的前一部分相同思路我的后续解决思路&#xff08;不建议&#xff09;若依后续解决思路&#xff08;建议&#xff09;若依后续解决思路举例 写作背景 平时我用case when then else end的机会也不多&#xff0c;之前用它来做对select结果进行计算&#xff…

chatgpt赋能python:Python代码20行,助力千万SEO从业者快速实现网站分析

Python代码20行&#xff0c;助力千万SEO从业者快速实现网站分析 SEO是现代数字营销的核心战略之一。对于千万从业者而言&#xff0c;网站分析是SEO实践的重要一环。而Python作为一门高效、简洁的编程语言&#xff0c;其丰富的第三方库和易学易用的语法使其成为网站分析的强大工…

【MySQL】从0到1打开数据库管理

目录 前言&#xff1a; 一.认识MySQL 二.安装MySQL数据库 三、启动和停止MySQL服务 3.1启动服务的两种方式 3.2停止服务的两种方式 四.链接客户端 4.1使用自带的命令行窗口 4.2使用系统自带的命令窗口 五.MySQL是存储数据的模型 六.SQL语言 结尾&#xff1a; 前言&a…

设计二:51单片机外部中断控制

目录 一、设计内容 二、中断相关知识 1、51单片机中断源 2、中断系统特殊功能寄存器 3、中断函数与函数调用区别 三、仿真原理图 四、程序设计 五、仿真结果 六、思考题 作者有话说 一、设计内容 本次设计使用2个按键&#xff0c;在无按键按下时&#xff0c;最下面一…

chatgpt赋能python:Python代码50行:如何使用Python进行SEO分析

Python代码50行&#xff1a;如何使用Python进行SEO分析 随着互联网的发展&#xff0c;Search Engine Optimization&#xff08;SEO&#xff09;已经成为企业网络营销策略中至关重要的一环。而Python作为一种全能的编程语言&#xff0c;已经逐渐成为许多SEO工程师的首选工具。本…

chatgpt赋能python:Python修改List的教程

Python修改List的教程 当涉及到Python编程时&#xff0c;对于处理和管理数据&#xff0c;List是一个非常常见和有用的数据结构。像大多数数据结构一样&#xff0c;有时需要对List进行修改&#xff0c;以便更好地满足程序的需求。因此&#xff0c;本文将向您展示如何使用Python…

数据结构与算法09:二叉树

目录 【树】 【二叉树】 二叉树的遍历 Go代码实现 二叉树的复杂度分析 【二叉搜索树】 Go代码实现 【每日一练&#xff1a;移除元素】 【树】 什么是树&#xff1f;这个不用解释了吧&#xff0c;马路两边种的都是树。数据结构里面的“树”和现实生活中的树类似&#…

chatgpt赋能python:Python会动的图形:如何让你的网站活力四射

Python会动的图形&#xff1a;如何让你的网站活力四射 如果你想让你的网站更具生命力、吸引力和互动性&#xff0c;一种非常有效的方式是使用动态图形。而Python有许多强大的库可以帮助你实现这一目标。在本篇文章中&#xff0c;我们将讨论Python会动的图形的好处、如何实现和…

过滤器JavaWeb:Filter与拦截器Spring:Intercepter

过滤器与拦截器若同时存在&#xff0c;先执行过滤器的放行前&#xff0c;再执行整个拦截器&#xff0c;最后再执行过滤器的放行后 过滤器会拦截所有资源&#xff08;包括静态资源&#xff09;&#xff0c;拦截器只会拦截Spring环境的资源 Filter的使用 1、创建一个类implement…

深蓝学院C++基础笔记 第 1 章 C++初探

第 1 章 C初探 1&#xff0e;从Hello World 谈起 Hello World: #include <iostream> int mian() { std::cout << "Hello World!" << std::endl; }函数: 一段能被反复调用的代码&#xff0c;可以接收输入&#xff0c;进行处理并(或)产生输出-返回…

Postgres vs MySQL

主要区别及示例 简而言之&#xff0c;Postgres 和 MySQL 之间的主要区别实际上归结为主索引和辅助索引的实现方式以及数据的存储和更新方式。 让我们进一步探讨这个问题。 但首先... 基础知识 索引是一种数据结构&#xff08;主要是 B 树&#xff09;&#xff0c;允许通过…