一、前言

我们在日常开发过程中,常常会碰到数据传输。为了保证数据的唯一性,我们需要生成一个签名和数据一起发送,对方获取数据后也通过同样的算法进行签名,然后对比两边的签名是否一致。如果一致则认为数据没有被串改或者是否为合法的数据,否则就是非法数据。

一般使用的模型各参数的值和名字拼起来,将name转成小写后按照字典(a-z)进行排序,得到一个字符串如下:name1+value1+&+string2+value2

然后加入私钥(一个双方约定的key)得到:name1+value1+&+string2+value2+&+key

最后使用md5函数生成一个sign串,这就是该字符串的md5签名。

注意:一般空值或者list类型数据不进行签名,这个自己排除掉。

二、代码示例

以下是整理好的代码。

首先通过反射拿出object字段的name和值,按照字典排序,再进行字符串拼接,最后使用md5函数进行签名。

public class SignUtil{

public static String signIt(Object object, Class<?> clazz, String key) {
        Logger logger = LoggerFactory.getLogger(SignUtil.class);
        //签名算法:
        if (object == null) {
            return null;
        }
        //首先获取所有key,顺便转成map,方便存储
        List<String> keys = new ArrayList<>();
        Map<String, Object> tmpMap = new HashMap<>();

        Field[] fields = clazz.getFields();
        for (Field f : fields) {
            if (!f.getName().equalsIgnoreCase("sign") && !f.getName().equalsIgnoreCase("list")) {
                try {
                    f.setAccessible(true);
                    Object value = f.get(object);
                    if (value != null && !"".equals(value.toString())) {
                        keys.add(f.getName());
                        tmpMap.put(f.getName(), f.get(object));
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        logger.debug("Generated tmpMap: {}", tmpMap);
        logger.debug("Generate keys list: {}", keys);

        //对key按照字典排序
        Collections.sort(keys);

        //拼接URL字符串
        List<String> keyValuePair = new ArrayList<>();

        for (String key : keys) {
            Object obj = tmpMap.get(key);
            String value = null;
            if (obj != null) {
                value = obj.toString();
            }

            if (StringUtils.isEmpty(value)) {
                //空参数不参与签名
                continue;
            }
            keyValuePair.add(key + "=" + value);
//            try {
//                keyValuePair.add(key + "=" + URLEncoder.encode(value, "utf-8"));
//            } catch (UnsupportedEncodingException e) {
//                logger.error("URL编码异常,原文:{}", value);
//                e.printStackTrace();
//            }
        }
        String stringA = StringUtils.join(keyValuePair, "&");
        logger.debug("签名前参数内容:{}", stringA);
        logger.debug("URL编码结果:{}", stringA);


        String stringSignTemp = stringA + "&key=" + key;
        logger.debug("需要md5的字符串:{}", stringSignTemp);
        String signValue = DigestUtils.md5Hex(stringSignTemp).toUpperCase();
        logger.debug("MD5签名结果:{}", signValue);

      
        return signValue;
    }
}

其中:

1、!f.getName().equalsIgnoreCase("sign") && !f.getName().equalsIgnoreCase("list")为object中不参与签名的字段,读者可以根据自己的需求进行添删。

2、注释的代码:

//            try {
//                keyValuePair.add(key + "=" + URLEncoder.encode(value, "utf-8"));
//            } catch (UnsupportedEncodingException e) {
//                logger.error("URL编码异常,原文:{}", value);
//                e.printStackTrace();
//            }

是对value值进行编码,因为有些中文需要在不同的编码中表现的值不同,所以导致签名也不通。

注意:DigestUtils.md5Hex 为站长工具类的函数,自己可以去网上查找响应的函数。