电商项目之有趣的支付签名算法

在这里插入图片描述

1 问题背景

在发起支付的时候,一般都需要对发送的请求参数进行加密或者签名,下文简称这个过程为“签名”。行业内比较普遍的签发算法有:
(1)按支付渠道给定的字段排序进行拼接,最后再拼一个密钥,形成一个待签名的字符串tobeSign,然后对这个tobeSign进行MD5编码。比如MD5(商户号+子应用ID+商户订单号+流水号+金额+币种+密钥)
(2)针对请求参数中的字段(仅针对第一层的字段,不需要针对字段里面的字段,即不需要递归),进行字典升序排序,用格式key=value&连接符拼接,最后再拼一个密钥,再用MD5编码比如MD5(a=value1&b=value2&key=密钥)
这次遇到一种比较有趣的签名算法,笔者认为是基于第(2)的变种,渠道方要求针对请求参数中的字段,如果该字段是对象类型,那么该字段里面的字段也要按字典升序排序进行拼接,相当于是递归字典升序,困难度有一点提升

2 思路

文字描述得有点抽象,可以结合第3小节的代码实现来看

遍历每一层字段,都用一个容器存起来,要按字典升序存。维护一个层序遍历的容器——双向队列。将前面升序的数据入队。遍历队列的每一元素,元素从队头出队,再遍历元素中的字段是否是对象类型或者数组类型,使用一个容器存起来,要按字典降序存,存完后使用头插法入队。使用头插法倒叙入队,每一次从队头遍历,那么每一次遍历都是升序遍历。

3 代码实现

解释:代码中的BeansUtil.bean2MapIgnoreEmptyStr()是将对象转成一个Map。SymbolConstant.EQUAL的值是一个=SymbolConstant.AND的值是一个&

public static String buildToBeSignStr(Object payReq) {
        // 将对象转成一个Map
        Map<String, String> map = BeansUtil.bean2MapIgnoreEmptyStr(payReq);
        TreeMap<String, String> treeMap = new TreeMap<>(map);

        List<String> result = new LinkedList<>();
        for (Map.Entry<String, String> entry : treeMap.entrySet()) {
            // 层序遍历容器
            Deque<Map.Entry<String, String>> bfsHolder = new LinkedList<>();
            // 结果暂存容器
            List<String> tmpResult = new LinkedList<>();

            // 入队
            bfsHolder.offer(entry);

            while (CollectionUtils.isNotEmpty(bfsHolder)) {
                Map.Entry<String, String> pollEntry = bfsHolder.poll();
                String pKey = pollEntry.getKey();
                String pVal = pollEntry.getValue();

                if (StringUtils.isNotBlank(pVal) && JSONValidator.from(pVal).validate()) {
                    // 是json串,仍需要继续解析
                    log.info("value of key:{} is json str.", pKey);
                    // 解析JSON字符串
                    Object parsedObject = JSON.parse(pVal);
                    boolean isJSONObject = parsedObject instanceof JSONObject;
                    boolean isJSONArray = parsedObject instanceof JSONArray;
                    if (isJSONObject || isJSONArray) {
                        Map<String, String> map1 = null;

                        if (isJSONObject) {
                            log.info("JSON字符串是一个对象");
                            JSONObject jsonObject = (JSONObject) parsedObject;
                            // 处理对象
                            map1 = BeansUtil.buildMapFromJsonStr(pVal);
                        } else if (isJSONArray) {
                            System.out.println("JSON字符串是一个数组");
                            JSONArray jsonArray = (JSONArray) parsedObject;
                            // 处理数组
                            for (Object o : jsonArray) {
                                map1 = BeansUtil.bean2MapIgnoreEmptyStr(o);
                            }
                        }
                        if (MapUtils.isNotEmpty(map1)) {
                            // 倒叙排序
                            Map<String, String> treeMap1 = new TreeMap<>(Comparator.reverseOrder());
                            treeMap1.putAll(map1);
                            // 插入到队头
                            Streams.of(treeMap1.entrySet()).forEach(bfsHolder::offerFirst);
                        }
                    } else {
                        tmpResult.add(pKey + SymbolConstant.EQUAL + pVal);
                    }
                } else {
                    tmpResult.add(pKey + SymbolConstant.EQUAL + pVal);
                }
            }

            if (CollectionUtils.isNotEmpty(tmpResult)) {
                String tmpResultStr = String.join(SymbolConstant.AND, tmpResult);
                result.add(tmpResultStr);
            }

        }

        if (CollectionUtils.isNotEmpty(result)) {
            return String.join(SymbolConstant.AND, result);
        }

        return "";
    }
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值