java,请求防篡改MD5加密sign签名最终解决方案
- 看完这一篇还没搞明白,记得给我发信息,我去找棵树一头撞死
-
- MD5算法
- 盐
- sign签名
- 核心注意的
- 下面是参数案例和工具类
看完这一篇还没搞明白,记得给我发信息,我去找棵树一头撞死
MD5算法
就是个加密 一个字符串MD5的编码格式加密,无论你那种语言运行,相同字符串加密起来结果都是一样的;## MD5算法
盐
盐就是一个随机字符串,没什么特殊的,sign签名的核心就是,原来的参数对象也好,Map也好,数组也好。一堆参数拼接成一个字符串,。然后再拼接上 加密盐。是的,就是拼接,一点多余的都没有。
再经过MD5编码生成的字符串就叫sign签名,
sign签名
sign来源上边已经说清楚了,生成sign的就是编码,啥特殊的都没有。唯一特殊的就是参数转换的字符串又拼接了一段提前存好的字符串,即加密盐。
核心注意的
这个过程,最需要注意的就是
1、对象参数字符串的拼接方式,用的什么字符间隔的
2、参数字符串拼接加密盐的间隔字符.(有的第三方是无间隔直接拼接)
3、编码加密好的sign,要放回到一开始的参数对象里卖,随请求一同发给第三方,让第三方检验,防被篡改参数
4、对象在拼接字符串的时候一定要有排序,大部分是正序,有一些是倒叙。
下面是参数案例和工具类
参数案例
// An highlighted block { //??????外层需要排序[代码里是正序] "appId": "168154240975", "timestamp": "1682653362", "sku_list": [ { //??????外层需要排序[代码里是倒叙] "sku_id": 6624, "sku_num": 10 },{//??????外层需要排序[代码里是倒叙] "sku_id": 6624, "sku_num": 10 } ], "user_name": "邓宝宝", "user_address": "邓宝宝的家", "user_realname": "邓大大", "pay_info": [ { //??????外层需要排序[代码里是倒叙] "amount": 10, "payTime": "2023-07-18 08:00:00", "paymentCode": "alipay", "tradeNo": "10082929392137123" } ] }
工具类
声明一点啊,难得发一篇文章。工具列好使了记得来点赞哈!
// An highlighted block import cn.hutool.crypto.SecureUtil; import cn.hutool.http.Header; import cn.hutool.http.HttpRequest; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.util.ObjectUtils; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.*; @Service @Slf4j public class MD5SignUtil { /* * post 调用 案例 */ public String queryOrderPostage(Map<String, Object> parm) { String shipAreaCode = (String) parm.get("shipAreaCode"); List skuList = (List) parm.get("skuList"); if (null==skuList||null==shipAreaCode||skuList.size()<=0||"".equals(shipAreaCode)){ return null; } Integer uuid = UUID.randomUUID().toString().replaceAll("-", "").hashCode(); Map<String, Object> map = new HashMap<>(); map.put("appId", "appid"); map.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000)); map.put("onceString", String.valueOf(uuid = uuid < 0 ? -uuid : uuid)); map.put("versions", "v2"); map.put("shipAreaCode", shipAreaCode);// List<Map> sku_list = new ArrayList<>(); for ( Object o : skuList) { Map<String, Object> sku = (Map<String, Object>) o; sku_list.add(sku); } map.put("sku_list", sku_list); System.out.println(map.toString()); String sign = createSign(map, ""); map.put("sign", sign); String s = JSONObject.toJSONString(map); String result = HttpRequest.post("POST_url") .header(Header.CONTENT_TYPE, "application/json") .body(JSONObject.toJSONString(map)) .execute() .body(); //{"status":"success","code":200,"message":"success","data":{"postage":0}} JSONObject jsonObject2 = JSON.parseObject(result); String status = (String) jsonObject2.get("data"); return status; } /* * GET 调用案例 */ public List<Map> queryGeocoding(String pid) throws UnsupportedEncodingException { Integer uuid= UUID.randomUUID().toString().replaceAll("-", "").hashCode(); Map<String, Object> map = new HashMap<>(); map.put("area_id",pid); map.put("appId","appId"); map.put("timestamp",String.valueOf(System.currentTimeMillis() / 1000)); map.put("onceString",String.valueOf(uuid = uuid < 0 ? -uuid : uuid)); map.put("versions", "v2"); String sign = createSign(map, "加密盐"); //生成sign map.put("sign", sign); String url = buildUrl("GET_url", map); String result = HttpRequest.get(url) .header(Header.CONTENT_TYPE, "application/json") .execute() .body(); JSONObject jsonObject2 = JSON.parseObject(result); List<Object> objectList = (List<Object>) jsonObject2.get("data"); String result1 = objectList.toString(); List<Map> mapList = new ArrayList<>(); mapList = JSON.parseArray(result1, Map.class); return mapList; } /* * TODO MD5 签名加密 生成 sign 不同平台接口接入需要使用需确认盐的拼接方式 */ public static String createSign(Map<String, Object> params, String secret_key) { params.remove("sign"); // 使用HashMap,并使用Arrays.sort排序 String[] sortedKeys = params.keySet().toArray(new String[]{}); Arrays.sort(sortedKeys); // TODO ??????? 外层倒序????? StringBuilder builder = new StringBuilder(); for (String key : sortedKeys) { if (ObjectUtils.isEmpty(params.get(key))) { continue; } Object object = params.get(key); if (object instanceof List) { builder = iterateParmStr(builder, key, object); }else if (object instanceof Map){ builder = iterateParmStr(builder, key, object); }else { builder.append(key).append("=").append(params.get(key)).append("&"); } } builder.deleteCharAt(builder.length()-1); builder.append(secret_key); System.out.println(builder); String signValue = SecureUtil.md5(builder.toString()).toUpperCase(); return signValue; } /* * TODO 雅! 雅不可言! * 此处迭代 内部 map 根据key 倒序 * 供应链的这个 sign签名真是麻烦,最外层正序,里层倒序。 */ public static StringBuilder iterateParmStr(StringBuilder builder, String name, Object object) { String oldname =name; if (object instanceof List) { List list = (List) object; for (int i = 0; i < list.size(); i++) { Object o = list.get(i); name = name + "[" + i + "]"; builder = iterateParmStr(builder, name, o); name=oldname; } } else if (object instanceof Map) { Map<String, Object> map = (Map<String, Object>) object; String[] mapKeys = map.keySet().toArray(new String[]{}); Arrays.sort(mapKeys,Collections.reverseOrder());// TODO ??????? 内层正序????? for (String key1 : mapKeys) { Object o = map.get(key1); name = name + "[" + key1 + "]"; builder = iterateParmStr(builder, name, o); name=oldname; } } else { builder.append(name).append("=").append(object.toString()).append("&"); } return builder; } /* * buildUrl TODO 参数构建 getURL 构建 */ private static String buildUrl(String url, Map<String, Object> querys) throws UnsupportedEncodingException { StringBuilder sbUrl = new StringBuilder(url); if (null != querys) { StringBuilder sbQuery = new StringBuilder(); for (Map.Entry<String, Object> query : querys.entrySet()) { if (0 < sbQuery.length()) { sbQuery.append("&"); } if (org.apache.commons.lang3.StringUtils.isBlank(query.getKey()) && !org.apache.commons.lang3.StringUtils.isBlank(String.valueOf(query.getValue()))) { sbQuery.append(query.getValue()); } if (!org.apache.commons.lang3.StringUtils.isBlank(query.getKey())) { sbQuery.append(query.getKey()); if (!org.apache.commons.lang3.StringUtils.isBlank(String.valueOf(query.getValue()))) { sbQuery.append("="); sbQuery.append(URLEncoder.encode(String.valueOf(query.getValue()), "utf-8")); } } } if (0 < sbQuery.length()) { sbUrl.append("?").append(sbQuery); } } return sbUrl.toString(); } }