利用fastJson保证数据字段顺序做api数据签名加密

原创 2020-01-08 17:28 阅读(1878)次

我们在开发api接口时,尤其是开放给第三方使用的接口,通常会对接口参数做签名加密校验。我看了下大部分人都只是把url上的参数放到TreeMap中做加密:

import org.apache.commons.codec.digest.DigestUtils;
import java.io.UnsupportedEncodingException;
import java.util.Map;
import java.util.TreeMap;
/**
 * @Author: rongrong
 * @Date: 2018/4/23
 * @Description:
 */
public class MD5Utils {
    private static void getDigest(TreeMap<String, String> map, String key, String charset){
        StringBuilder sb = new StringBuilder();
        for (Map.Entry entry : map.entrySet()) {
            sb = sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
        }
        sb.append("key").append("=").append(key);
        System.out.println("拼接后的字符:"+sb.toString());
        String sign = DigestUtils.md5Hex(getContentBytes(sb.toString(), charset));
        System.out.println("加密后的签名:"+sign);
    }
    private static byte[] getContentBytes(String content, String charset) {
        if (charset == null || "".equals(charset)) {
            return content.getBytes();
        }
        try {
            return content.getBytes(charset);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
        }
    }
    public static void main(String[] args) {
        //treeMap默认是key升序排序 ,如果需要降序,可以使用Comparator中的compare方法
        TreeMap<String,String> map = new TreeMap<String, String>();
        map.put("name", "zychen");
        map.put("password", "123456");
        map.put("project", "base");
        map.put("tenantId", "192319387131");
        getDigest(map, "helloWorld","utf-8");
    }
}
像上面签名方法只是做简简单单的数据签名,TreeMap只有一层数据结构,对于复杂的嵌套的数据结构,比如TreeMap中还是List,List还有Map这种数据结构,上面的签名方法就不行了,它无法保证TreeMap中list里的Map也是顺序的,就会出现签名结果没办法保证两次加密结果一致,例如如下要对这种复杂的数据签名:

		Map m = new HashMap();
		
		m.put("cx",1);
		m.put("x",1);
		m.put("cc",1);
		m.put("cT",1);
		m.put("ct",1);
		
		List<Map> list = new ArrayList();
		
		
		Map m1 = new HashMap();
		m1.put("cX", 1);
		m1.put("cx", 1);
		m1.put("ac", 1);
		m1.put("b", 1);
		Map m2 = new HashMap();
		m2.put("cx", 1);
		m2.put("cX", 1);
		m2.put("ac", 1);
		m2.put("b", 1);
		list.add(m1);
		list.add(m2);
		
		m.put("a",list);

那有没有办法对这种复杂的数据进行签名呢?

当然有,我们也不需要对嵌套的结构进行解析就可以做到,那就是可以使用fastjson生成顺序的json,这样就可以保证两次签名一致了,要把上面的Map m数据签名加密,具体代码如下:

            String json = JSON.toJSONString(m);
            TreeMap parseObject = JSON.parseObject(json, TreeMap.class, Feature.OrderedField);
            json = JSON.toJSONString(parseObject);//排序后的json

这样就可以拿到所有字段是排序过的json了,这样再拿去签名就可以了。

解释一下:JSON.parseObject(json, TreeMap.class, Feature.OrderedField);中的TreeMap是为了保证最外层的map是顺序的,Feature.OrderedField是设置fastJson在生成json时嵌套的字段也是排序的。