上傳數(shù)據(jù)參數(shù)簽名

目前越來(lái)越多的接口請(qǐng)求使用了簽名的方式,整個(gè)思路就是我們和后臺(tái)約定一個(gè)對(duì)參數(shù)的簽名方式,簽名完成以后在所有參數(shù)的后面多一個(gè)本地完成的簽名字符串作為參數(shù)一起傳給后臺(tái),后臺(tái)通過(guò)同樣的簽名方式也生成簽名,跟我們傳給后臺(tái)的這個(gè)簽名參數(shù)做對(duì)比,如果一致則代表參數(shù)沒(méi)有被修改,一定程度保證了安全性。最近公司安全部門讓我們做一下請(qǐng)求參數(shù)簽名,個(gè)人覺(jué)得這部分基本都是這樣的用法,所以總結(jié)出來(lái):

先說(shuō)一下加密簽名的這個(gè)流程:
1: 對(duì)所有外層參數(shù)按照參數(shù)的ASCII從小到大排序
2:對(duì)排序后的參數(shù)列表去除為空的參數(shù)并用這個(gè)進(jìn)行MD5加密,然后再轉(zhuǎn)成大寫
3:用第一步排序后的參數(shù)列表拼上第二步生成的簽名作為一個(gè)json字符串

因?yàn)槲覀兒笈_(tái)要求的使用RequestBody的形式請(qǐng)求,輸入的是json字符串,所以我寫了一個(gè)工具類先是把這個(gè)json字符串轉(zhuǎn)成Map,然后再排序。方法如下:
/**
* 將RequestBody轉(zhuǎn)成map并按照key的字母順序進(jìn)行排序得出新的map
*
* @param json
* @return
*/
public static Map<String, Object> sortParamsMap(String json) {
Map<String, Object> objectMap = GsonUtils.jsonToMap(json);
Map<String, Object> newMap = sortMapByKey(objectMap);
return newMap;
}

/**
* 使用 Map按key進(jìn)行排序
*
* @param map
* @return
*/
public static Map<String, Object> sortMapByKey(Map<String, Object> map) {
if (map == null || map.isEmpty()) {
return null;
}
Map<String, Object> sortMap = new TreeMap<>(new MapKeyComparator());
sortMap.putAll(map);
return sortMap;
}

static class MapKeyComparator implements Comparator<String> {

    @Override
    public int compare(String str1, String str2) {

        return str1.compareTo(str2);
    }
}

排序好了以后我們需要去除Map中value為空的項(xiàng),用不為空的項(xiàng)再次轉(zhuǎn)成json字符串然后MD5加密:
這里涉及到一個(gè)Map遍歷刪除的方法,需要注意的是next方法在程序的一次流程中只能被調(diào)用一次,否則它會(huì)去找下一項(xiàng)可能導(dǎo)致判斷的有的判斷不準(zhǔn)確甚至如果下一項(xiàng)為空可能會(huì)導(dǎo)致報(bào)錯(cuò):NoSuchElementException,所以我們可以這樣使用:
Map.Entry<String,Object> entry = iterator.next();
Object value = entry.getValue();

/**
 * 帶簽名的傳輸參數(shù)
 *
 * @param json
 * @return
 */
public static String paramsJson(String json) {
    Map<String, Object> sortMap = = GsonUtils.jsonToMap(json);
    Map<String, Object> newMap = sortParamsMap(json);
    Iterator<Map.Entry<String, Object>> iterator = newMap.entrySet().iterator();
    while (iterator.hasNext()) {
        Map.Entry<String,Object> entry = iterator.next();
        Object value = entry.getValue();
        if (value == null || value.equals("")) {
            iterator.remove();
        }
    }
    String sign = getSign(newMap);
    sortMap.put("sign", sign);
    String result = GsonUtils.ObjectToJsonStr(sortMap);
    return result;
}

/**
 * @param newMap
 * @return 對(duì)排序后的參數(shù)進(jìn)行MD5加密生成大寫字母的簽名
 */
public static String getSign(Map<String, Object> newMap) {
    String paramsStr = GsonUtils.ObjectToJsonStr(newMap);
    return MD5Utils.getMd5Value(paramsStr).toUpperCase();
}

這里一開始使用的是Gson的fromJson方法把json轉(zhuǎn)成map,可是出現(xiàn)了一個(gè)解析的問(wèn)題,這個(gè)方法會(huì)把int的比如1轉(zhuǎn)成double的1.0,這樣后面MD5后就不一樣了,所以就找度娘怎么解決,找了半天試了有四五種,終于讓我找到一個(gè)可以的方案:
解析這塊兒所有的代碼如下:

public static Map<String, Object> jsonToMap(String json) {
Map<String,Object> map = fromJson(json,new TypeToken<Map<String, Object>>(){});
return map;
}

/**
 * json字符串轉(zhuǎn)list或者map
 *
 * @param json
 * @param typeToken
 * @return
 */
public static <T> T fromJson(String json, TypeToken<T> typeToken) {
    Gson gson = new GsonBuilder()
    /**
     * 重寫map的反序列化
     */
    .registerTypeAdapter(new TypeToken<Map<String, Object>>() {
    }.getType(), new GsonTypeAdapter()).create();
    //MapTypeAdapter是繼承了TypeAdapter類,并單獨(dú)處理Map類型的反序列化。注意:目前只綁定了Map類型,其子類(HashMap)的處理沒(méi)有變化。具體代碼見本文最后或GitHub(發(fā)布后會(huì)給出地址)。
    return gson.fromJson(json, typeToken.getType());
}

//重點(diǎn)是這里
public class GsonTypeAdapter extends TypeAdapter<Object> {

@Override
public Object read(JsonReader in) throws IOException {
    JsonToken token = in.peek();
    switch (token) {
        case BEGIN_ARRAY:
            List<Object> list = new ArrayList<Object>();
            in.beginArray();
            while (in.hasNext()) {
                list.add(read(in));
            }
            in.endArray();
            return list;

        case BEGIN_OBJECT:
            Map<String, Object> map = new LinkedTreeMap<String, Object>();
            in.beginObject();
            while (in.hasNext()) {
                map.put(in.nextName(), read(in));
            }
            in.endObject();
            return map;

        case STRING:
            return in.nextString();

        case NUMBER:
            /**
             * 改寫數(shù)字的處理邏輯,將數(shù)字值分為整型與浮點(diǎn)型。
             */
            double dbNum = in.nextDouble();

            // 數(shù)字超過(guò)long的最大值,返回浮點(diǎn)類型
            if (dbNum > Long.MAX_VALUE) {
                return dbNum;
            }

            // 判斷數(shù)字是否為整數(shù)值
            long lngNum = (long) dbNum;
            if (dbNum == lngNum) {
                return lngNum;
            } else {
                return dbNum;
            }

        case BOOLEAN:
            return in.nextBoolean();

        case NULL:
            in.nextNull();
            return null;

        default:
            throw new IllegalStateException();
    }
}

@Override
public void write(JsonWriter out, Object value) throws IOException {
    // 序列化無(wú)需實(shí)現(xiàn)
}

}

另外還需要注意的一點(diǎn)是為了保證我們客戶端的簽名和后臺(tái)的簽名一致,務(wù)必要保證MD5的編碼格式跟我們的保持一致,我這里的MD5方法提供出來(lái):
/**
* 32位MD5加密方法
* 16位小寫加密只需getMd5Value("xxx").substring(8, 24);即可
*
* @param sSecret
* @return
*/
public static String getMd5Value(String sSecret) {
try {
MessageDigest bmd5 = MessageDigest.getInstance("MD5");
bmd5.update(sSecret.getBytes("UTF-8"));
int i;
StringBuffer buf = new StringBuffer();
// 加密
byte[] b = bmd5.digest();
for (int offset = 0; offset < b.length; offset++) {
i = b[offset];
if (i < 0)
i += 256;
if (i < 16)
buf.append("0");
buf.append(Integer.toHexString(i));
}
return buf.toString();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}

下面是簽名的類,包含上面涉及到的所有簽名方法:
/**

  • @author : zhengangyao

  • @e-mail : 139745815@ qq.com

  • @date : 2018/12/1718:19

  • @desc : 請(qǐng)求參數(shù)簽名工具類

  • @version: 1.1.0
    */
    public class SignUtil {

    /**

    • 帶簽名的傳輸參數(shù)
    • @param json
    • @return
      */
      public static String paramsJson(String json) {
      Map<String, Object> sortMap = = GsonUtils.jsonToMap(json);
      Map<String, Object> newMap = sortParamsMap(json);
      Iterator<Map.Entry<String, Object>> iterator = newMap.entrySet().iterator();
      while (iterator.hasNext()) {
      Map.Entry<String,Object> entry = iterator.next();
      Object value = entry.getValue();
      if (value == null || value.equals("")) {
      iterator.remove();
      }
      }
      String sign = getSign(newMap);
      sortMap.put("sign", sign);
      String result = GsonUtils.ObjectToJsonStr(sortMap);
      return result;
      }

    /**

    • @param newMap
    • @return 對(duì)排序后的參數(shù)進(jìn)行MD5加密生成大寫字母的簽名
      */
      public static String getSign(Map<String, Object> newMap) {
      String paramsStr = GsonUtils.ObjectToJsonStr(newMap);
      return MD5Utils.getMd5Value(paramsStr).toUpperCase();
      }

    /**

    • 將RequestBody轉(zhuǎn)成map并按照key的字母順序進(jìn)行排序得出新的map
    • @param json
    • @return
      */
      public static Map<String, Object> sortParamsMap(String json) {
      Map<String, Object> objectMap = GsonUtils.jsonToMap(json);
      Map<String, Object> newMap = sortMapByKey(objectMap);
      return newMap;
      }

    /**

    • 使用 Map按key進(jìn)行排序
    • @param map
    • @return
      */
      public static Map<String, Object> sortMapByKey(Map<String, Object> map) {
      if (map == null || map.isEmpty()) {
      return null;
      }
      Map<String, Object> sortMap = new TreeMap<>(new MapKeyComparator());
      sortMap.putAll(map);
      return sortMap;
      }

    static class MapKeyComparator implements Comparator<String> {

     @Override
     public int compare(String str1, String str2) {
    
         return str1.compareTo(str2);
     }
    

    }
    }

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容