Java开发SDK详解->SDK开发

一、前言

  • 前面已经将服务端开发好了(服务端开发),现在我们来开发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>
       <version>1.7.20</version>
   </dependency>
   <!-- 添加logback-classic依赖 -->
   <dependency>
       <groupId>ch.qos.logback</groupId>
       <artifactId>logback-classic</artifactId>
       <version>1.2.3</version>
   </dependency>
   <!-- 添加logback-core依赖 -->
   <dependency>
       <groupId>ch.qos.logback</groupId>
       <artifactId>logback-core</artifactId>
       <version>1.2.3</version>
   </dependency>
   <!--lombok支持-->
   <dependency>
       <groupId>org.projectlombok</groupId>
       <artifactId>lombok</artifactId>
       <version>1.16.16</version>
   </dependency>
   <!--工具类-->
   <dependency>
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-lang3</artifactId>
       <version>3.4</version>
   </dependency>
</dependencies>
2.2.2 SysUserClient

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

package com.lh.hope.client;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.lh.hope.common.ReturnCodeEnum;
import com.lh.hope.domain.SysUser;
import com.lh.hope.domain.SysUserDTO;
import com.lh.hope.domain.common.ApiRequest;
import com.lh.hope.domain.common.BaseResponse;
import com.lh.hope.domain.common.PageModel;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class SysUserClient extends ClientAbstract {

    public static BaseResponse<PageModel<SysUser>> queryUserList(ApiRequest<SysUserDTO> request) {
        try {
            String str = post(request);
            return JSON.parseObject(str, new TypeReference<BaseResponse<PageModel<SysUser>>>() {
            });
        } catch (Exception e) {
            log.error("SysUserClient queryUserList is exception! request={}", request);
            return BaseResponse.error(ReturnCodeEnum.SYSTEM_ERROR);
        }
    }
}
2.2.3 ClientAbstract

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

package com.lh.hope.client;

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

@Slf4j
class ClientAbstract {

    static String post(ApiRequest request) {
        HopeRequest hopeRequest = HopeRequest.builder()
                .appId(request.getAppId())
                .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(ReturnCodeEnum.API_ERROR);
        }
        HopeResponse hopeResponse = JSON.parseObject(s, HopeResponse.class);
        if (!hopeResponse.isSuccess()) {
            log.error("client post api error! hopeResponse={}", hopeResponse);
            throw new HopeException(ReturnCodeEnum.API_ERROR.getCode(), hopeResponse.getMessage());
        }
        return RsaUtil.decrypt(request.getPublicKey(), hopeResponse.getData());
    }
}
2.2.4 HttpUtil

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

package com.lh.hope.utils;

import lombok.extern.slf4j.Slf4j;

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

@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.lh.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;

@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.lh.hope;

import com.alibaba.fastjson.JSON;
import com.lh.hope.client.SysUserClient;
import com.lh.hope.domain.SysUser;
import com.lh.hope.domain.SysUserDTO;
import com.lh.hope.domain.common.ApiRequest;
import com.lh.hope.domain.common.BaseResponse;
import com.lh.hope.domain.common.PageModel;

public class App {

    /**
     * 公钥
     */
    private static final String PUBLIC_KEY_STRING = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCllRJyNyA5/kOKpF+VV322IN7fownz5GMltjnWLHJPE+xdusVYHz/3C0ck27sv7mHP0TrJ7PLxUHyeUJ9PGOZ2fyrBRikKNE4ce1ihNgQxorIJ68G+70eHyOr65mQxRYa4lUOHMMPHgicN/2vGCjwL/ET8eQU0yIRAoOnO8avAuQIDAQAB";
    
    public static void main(String[] args) {
        SysUserDTO dto = new SysUserDTO();
        dto.setStatus(0);
        ApiRequest<SysUserDTO> request = ApiRequest.<SysUserDTO>builder()
                .appId("000001")
                .url("http://localhost:8081/api/user/queryUserList")
                .publicKey(PUBLIC_KEY_STRING)
                .data(dto)
                .build();
        BaseResponse<PageModel<SysUser>> pageModelBaseResponse = SysUserClient.queryUserList(request);
        System.out.println(JSON.toJSONString(pageModelBaseResponse));
    }

}
2.2.6 HopeRequest

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

package com.lh.entity.api;

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

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

    /**
     * 加密后业务相关的入参
     */
    private String data;
}
2.2.6 HopeResponse
package com.lh.entity.api;

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

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

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

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

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

}
2.2.7 ApiRequest

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

package com.lh.hope.domain.common;

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

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

    private String url;
    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文件夹下
    在这里插入图片描述

三、最后

现在一个简单的SDK已经开发打包好了,服务端之前也已经开发并启动了,现在就剩下最后一步客户端引用SDK测试了。

  • 22
    点赞
  • 163
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Fabric Java SDK 是为 Hyperledger Fabric 区块链平台开发Java SDK,它提供了访问 Fabric 网络的所有必要接口,包括身份验证、链码调用、交易提交等。Spring Boot 是一个基于 Spring 框架的快速开发应用程序的工具,它提供了诸多便利的特性,如自动配置、快速开发等。 在使用 Fabric Java SDK 开发 Spring Boot 项目时,我们可以采用以下步骤: 1. 配置 Maven 依赖 在 pom.xml 文件中添加以下依赖: ```xml <dependency> <groupId>org.hyperledger.fabric-sdk-java</groupId> <artifactId>fabric-sdk-java</artifactId> <version>2.2.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> ``` 2. 配置 Fabric SDK 在 Spring Boot 项目中,我们可以使用 @Configuration 注解配置 Fabric SDK。在配置中,我们需要指定以下参数: - Fabric 网络的 CA 证书 - Fabric 网络的 MSP 证书 - Fabric 网络的连接配置文件 ```java @Configuration public class FabricConfig { private static final String CONNECTION_PROFILE_PATH = "connection-profile.yaml"; private static final String MSP_PATH = "/msp"; private static final String CA_ORG1_URL = "http://localhost:7054"; private static final String CHANNEL_NAME = "mychannel"; @Bean public NetworkConfig getNetworkConfig() throws Exception { Path connectionProfilePath = Paths.get(CONNECTION_PROFILE_PATH); return NetworkConfig.fromYamlFile(connectionProfilePath.toFile()); } @Bean public User getUser(NetworkConfig networkConfig) throws Exception { UserContext userContext = new UserContext(); userContext.setName("user1"); userContext.setAffiliation("org1"); userContext.setMspId("Org1MSP"); Path mspPath = Paths.get(networkConfig.getOrganizationInfo("Org1").getMspId(), MSP_PATH); userContext.setEnrollment(new Enrollment() { @Override public PrivateKey getKey() { try { return loadPrivateKey(mspPath); } catch (Exception e) { throw new RuntimeException(e); } } @Override public String getCert() { try { return new String(loadCert(mspPath), "UTF-8"); } catch (Exception e) { throw new RuntimeException(e); } } }); return userContext; } @Bean public FabricGateway getGateway(NetworkConfig networkConfig, User user) throws Exception { Gateway.Builder builder = Gateway.createBuilder(); builder.identity(user); builder.networkConfig(networkConfig); return builder.connect().get(); } @Bean public Contract getContract(FabricGateway gateway) throws Exception { return gateway.getNetwork(CHANNEL_NAME).getContract("mychaincode"); } private byte[] loadCert(Path mspPath) throws Exception { Path certPath = mspPath.resolve(Paths.get("signcerts", "cert.pem")); return Files.readAllBytes(certPath); } private PrivateKey loadPrivateKey(Path mspPath) throws Exception { Path keyPath = mspPath.resolve(Paths.get("keystore", "priv.key")); byte[] keyBytes = Files.readAllBytes(keyPath); return getPrivateKeyFromBytes(keyBytes); } private PrivateKey getPrivateKeyFromBytes(byte[] keyBytes) throws Exception { PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory factory = KeyFactory.getInstance("EC"); return factory.generatePrivate(spec); } } ``` 3. 实现接口方法 在接口方法中,我们可以使用 Fabric SDK 提供的 API 实现对链码的调用、交易的提交等操作。 ```java @RestController public class MyController { private static final String CHAINCODE_NAME = "mychaincode"; private static final String FUNCTION_NAME = "invoke"; private static final String KEY = "key1"; private static final String VALUE = "value1"; @Autowired private Contract contract; @PostMapping("/set") public String setValue() throws Exception { contract.submitTransaction(FUNCTION_NAME, KEY, VALUE); return "success"; } @GetMapping("/get") public String getValue() throws Exception { byte[] result = contract.evaluateTransaction(FUNCTION_NAME, KEY); return new String(result, "UTF-8"); } } ``` 以上就是使用 Fabric Java SDK 开发 Spring Boot 项目的流程。通过这种方式,我们可以方便地实现对 Fabric 网络的访问,并快速开发出符合需求的应用程序。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值