java,请求防篡改MD5加密sign签名最终解决方案

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();
    }
}