最近对接多个快递公司,由于需要引入多个官方sdk,抽取了一个统一工具类
枚举
package com.joyi.courier.common.enums;
import lombok.Getter;
/**
* 编码枚举
*
* @author Wangnz
*/
@Getter
public enum CodecType {
/**
* 编码枚举
*/
MD5("MD5"),
URL("URL"),
BASE64("base64");
/**
* 描述
*/
private final String desc;
CodecType(String desc) {
this.desc = desc;
}
}
工具类
package com.joyi.courier.util;
import cn.hutool.core.util.ObjectUtil;
import com.joyi.courier.common.enums.CodecType;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.function.Consumer;
/**
* 编码工具类
* 未实现的加密方式 自己添加对应方法即可
*
* @author Wangnz
*/
public class CodecUtils {
private static final Map<CodecType, Consumer<byte[]>> CODEC_METHOD_MAP = new HashMap<>();
private static final ThreadLocal<byte[]> SIGN_THREAD_LOCAL = new ThreadLocal<>();
static {
CODEC_METHOD_MAP.put(CodecType.MD5, CodecUtils::md5);
CODEC_METHOD_MAP.put(CodecType.URL, CodecUtils::urlEncode);
CODEC_METHOD_MAP.put(CodecType.BASE64, CodecUtils::base64Encode);
}
/**
* 签名制作
*
* @param str 加密字符串
* @param codecTypes 加密顺序
* @return
*/
public static String signCreate(String str, CodecType... codecTypes) {
if (codecTypes == null || codecTypes.length == 0) {
return str;
}
try {
SIGN_THREAD_LOCAL.set(getBytesUtf8(str));
for (CodecType codecType : codecTypes) {
Consumer<byte[]> run = CODEC_METHOD_MAP.get(codecType);
if (run == null) {
throw new RuntimeException("codecType error");
}
run.accept(SIGN_THREAD_LOCAL.get());
}
//如果最后一步为MD5 则转换为16进制
if (CodecType.MD5.equals(codecTypes[codecTypes.length - 1])) {
return bytesToHex(SIGN_THREAD_LOCAL.get());
} else {
return newStringUtf8(SIGN_THREAD_LOCAL.get());
}
} catch (RuntimeException e) {
return null;
} finally {
SIGN_THREAD_LOCAL.remove();
}
}
/**
* 验签工具
*
* @param reqAndSecretStr 快递公司请求加密前的字符串(一般为 请求bodyJson 拼接 Secret)
* @param reqSign 快递公司请求签名
* @param codecTypes 加密方式顺序
* @return
*/
public static boolean checkSign(String reqAndSecretStr, String reqSign, CodecType... codecTypes) {
if (!ObjectUtil.isAllNotEmpty(reqAndSecretStr, reqSign) || codecTypes.length == 0) {
return false;
}
String real = signCreate(reqAndSecretStr, codecTypes);
return reqSign.equals(real);
}
/**
* md5
*
* @param str
*/
public static void md5(byte[] str) {
MessageDigest md5 = null;
try {
md5 = MessageDigest.getInstance("MD5");
md5.update(str);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("md5 error");
}
SIGN_THREAD_LOCAL.set(md5.digest());
}
/**
* urlEncode
*
* @param str
*/
public static void urlEncode(byte[] str) {
SIGN_THREAD_LOCAL.set(getBytesUtf8(URLEncoder.encode(newStringUtf8(str), StandardCharsets.UTF_8)));
}
/**
* base64
*
* @param str
*/
public static void base64Encode(byte[] str) {
SIGN_THREAD_LOCAL.set(Base64.getEncoder().encode(str));
}
/**
* 将字节数组转换为十六进制字符串
*
* @param bytes byte
* @return str
*/
private static String bytesToHex(byte[] bytes) {
StringBuilder result = new StringBuilder();
for (byte b : bytes) {
result.append(String.format("%02x", b));
}
return result.toString();
}
public static String newStringUtf8(byte[] str) {
return new String(str, StandardCharsets.UTF_8);
}
public static byte[] getBytesUtf8(String str) {
return str.getBytes(StandardCharsets.UTF_8);
}
public static String UUID() {
return UUID.randomUUID().toString().replaceAll("-", "");
}
/**
* 拼接字符串 逗号分隔
* 接收其他类型的自己重载
*
* @param list
* @return
*/
public static String appendComma(List<String> list) {
StringJoiner joiner = new StringJoiner(",");
list.forEach(joiner::add);
return joiner.toString();
}
public static void main(String[] args) {
//顺丰
System.out.println(signCreate("111", CodecType.URL, CodecType.MD5, CodecType.BASE64));
//极兔
System.out.println(signCreate("111", CodecType.MD5, CodecType.BASE64));
//韵达
System.out.println(signCreate("111", CodecType.MD5));
}
}