接口字段处理

1.构建请求对象实体

Java是面向对象的语言,所以这一步是理所当然的,没有为什么。根据请求报文字段,可以创建请求对象如下:

import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @Title: CommonReq
 * @author: HOLiC
 * @date: 2020/7/24
 */
@Data
@NoArgsConstructor
public class CommonReq {
    private String encType = "aes";
    private String signType = "md5";
    private String signData;
    private String busiName;
    private String busiData;
    private String timeStamp;
    private String nonce;
    private String clientId = "holic_01";
}

有些字段的值是固定的,所以在实体类中先行赋值了。

2.创建AES加解密工具类

AES是一种对称加密算法,感兴趣可以自行百度更多细节。这里我们用AES对业务报文进行加密处理。

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Key;

/**
 * AES对称加密算法
 * */
public class AESCoder {

    private static final String KEY_ALGORITHM = "AES";

    private static final String CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";

    /* *
     * @Description: 解密
     * @author: HOLiC
     * @date: 2020/7/24
     * @Param: [data, key]
     * @return: java.lang.String
     **/
    public static String decrypt(String data, String key) throws Exception {
        // 还原密钥
        Key k = toKey(key);
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        // 初始化,设置为解密模式
        cipher.init(Cipher.DECRYPT_MODE, k);
        // data转为字节数组
        byte[] dataByte = Base64.decodeBase64(data);
        // 执行操作
        byte[] decryptByte = cipher.doFinal(dataByte);

        return  new String(decryptByte, StandardCharsets.UTF_8);
    }

    /* *
     * @Description: 加密
     * @author: HOLiC
     * @date: 2020/7/24
     * @Param: [data, key]
     * @return: java.lang.String
     **/
    public static String encrypt(String data, String key) throws Exception {
        // 还原密钥
        Key k = toKey(key);
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        // 初始化,设置为加密模式
        cipher.init(Cipher.ENCRYPT_MODE, k);
        // 执行操作
        byte[] encryptByt = cipher.doFinal(data.getBytes());

        return Base64.encodeBase64String(encryptByt);
    }

    /**
     * 生成密钥
     * @return byte[] 二进制密钥
     * */
    public static byte[] initKey() throws Exception {
        // 实例化密钥生成器
        KeyGenerator kg = KeyGenerator.getInstance(KEY_ALGORITHM);
        // 初始化密钥生成器,AES要求密钥长度为128位、192位、256位
        // AES/ECB/PKCS5Padding的填充方式不支持256位秘钥
        kg.init(192);
        // 生成密钥
        SecretKey secretKey = kg.generateKey();
        // 获取二进制密钥编码形式
        return secretKey.getEncoded();
    }

   /* *
    * @Description: 秘钥转换
    * @author: HOLiC
    * @date: 2020/7/24
    * @Param: [key]
    * @return: java.security.Key
    **/
   private static Key toKey(String key) {
        byte[] keyBytes = key.getBytes(StandardCharsets.US_ASCII);
        return new SecretKeySpec(keyBytes,KEY_ALGORITHM);
    }

}

工具类亲测可用,绝对无害。

3.创建字符串和实体对象转换工具类

加密工具只操作字符串,我们的请求报文是个实体,所以需要这个工具类来实现转换。 具体的实现很简单:

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * @Title: JsonUtils
 * @author: HOLiC
 * @date: 2020/7/24
 */
public class JsonUtils {

    private static final ObjectMapper mapper = new ObjectMapper();

    /* *
     * @Description: 字符串转实体对象
     * @author: HOLiC
     * @date: 2020/7/24
     * @Param: [jsonStr, clazz]
     * @return: T
     **/
    public static  <T> T jsonStrToEntity(String jsonStr, Class<T> clazz) throws JsonProcessingException {
            return mapper.readValue(jsonStr, clazz);
    }

    public static String entityToJsonStr(Object data) throws JsonProcessingException {
        return mapper.writeValueAsString(data);
    }
}

有编译异常直接在方法签名里抛出,让service层去处理。

4.报文字段处理

工具都准备好了,可以正式开工了。

import com.holic.blog.entity.CommonReq;
import org.apache.commons.codec.digest.DigestUtils;

import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Random;
import java.util.TreeMap;

/**
 * @title DealCommonReq
 * @author HOLiC
 * @date 2020/7/24
 */
public class DealCommonReq {

    private DealCommonReq() {
    }

    private static final String aesKey = "9dLY2oHO0dKHFxG8XdJY/PPuk+wQVa7H";

    private static final String md5Key = "2oO0dG8JPukQVa7H";

    public static CommonReq getCommonReq(Object data, String busiName) throws Exception {
        // 1.先将真正的业务报文转成字符串;异常直接抛出,让业务层去处理
        String jsonStr = JsonUtils.entityToJsonStr(data);
        // 2.加密;
        String encrypt = AESCoder.encrypt(jsonStr, aesKey);
        // 3.请求报文字段排序;使用Treemap即可,默认就是ASCII的升序。需要降序可重写Comparator。
        CommonReq req = new CommonReq();
        req.setBusiData(encrypt);
        req.setBusiName(busiName);
        req.setTimeStamp(""+System.currentTimeMillis());
        req.setNonce(""+new Random().nextInt(99999999)); // 8位的随机数

        TreeMap<String, Object> map = new TreeMap<>();
        Class<?> reqClass = req.getClass();
        Field[] fields = reqClass.getDeclaredFields(); //反射拿到所有字段
        for (Field obj : fields ) {
            obj.setAccessible(true);
            String key = obj.getName();
            Object value = obj.get(req);
            if (value == null) continue;
            map.put(key, value);
        }
        // 4.请求报文拼接,形如key1=value1&key2=value2&......
        StringBuffer buffer = new StringBuffer();
        for (Map.Entry field: map.entrySet()) {
            buffer.append(field.getKey()).append("=").append(field.getValue()).append("&");
        }
        String temp = buffer.substring(0, buffer.length() - 1);
        // 5.签名
        String text = temp + md5Key;
        String hex = DigestUtils.md5Hex(text.getBytes(StandardCharsets.UTF_8));
        req.setSignData(hex);
        return req;
    }

}

对外的接口,aesKey和md5Key的值通常由双方来确定,直接给定或者规定生成规则,肯定是一致的。需要注意,这两个key需要绝对保密。

大概就这么多吧,写这篇一是希望帮到别人,二就是万一自己下次遇到类似的处理,能回想起来,不用从头再来。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值