java sdk:Java开发SDK详解

一、服务端开发

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: */@Slf4jpublic 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@AllArgsConstructorpublic 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@AllArgsConstructorpublic 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: */@Datapublic 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: */@Slf4jpublic 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: */@Slf4jpublic 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: */@Slf4jclass 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: */@Slf4jpublic 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: */@Slf4jpublic 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@AllArgsConstructorpublic 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@AllArgsConstructorpublic 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@AllArgsConstructorpublic 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博客

相关推荐

最新

相关文章