zoukankan      html  css  js  c++  java
  • MD5是个好东西 / MD5 is a nice guy

    md5是一种摘要生成算法,通过对消息生成唯一摘要,可校验消息是否被篡改。


    众所周知,md5广泛用在http接口通讯的安全控制上,通过在签名原始串后加上商户通信秘钥,进行MD5运算,形成的摘要字符串即为签名结果。接口双方据此来判断报文是否一致。

    只要消息不变,其md5值就不变。正因为这一点,md5也不光用在接口通讯上,我们的程序中也大可借用这一特性来保证消息的一致。

    举一个例子,我们经常有判重机制,比如验证重复请求,比如保证一段逻辑不能被重复执行(汇票系统里,提醒消息不能重发;定时任务在特定时间段内被重复触发,但其内容逻辑不能重复执行)。这些场景,用redis分布式锁配合md5将是完美的解决方案。

        public boolean remind(TradeOrder order, TradeRemindTypeEnum remindTypeEnum) {
    
            log.info("订单发送提醒开始。orderNo={},通知类型={}", order.getOrderNo(), remindTypeEnum);
    
            int exSeconds = 60 * 60;//过期时间为1小时
            //如果key不存在,执行set操作,返回字符串OK;如果key已经存在,则返回null
            String redisKey = remindTypeEnum + "_" + order.getOrderNo() + "_" + MD5Util.md5(JSONObject.toJSONString(order));
            if (null == JedisClusterUtil.getJedisCluster().set(redisKey, UUID.randomUUID().toString(), "NX", "EX", exSeconds)) {
                log.info("订单已经提醒过,本次不再提醒。 {}", redisKey);
                return false;
            }
            
            ....发送提醒消息...
            
        }
    View Code

    再举一个例子,票据贴息金额可按年利率或每十万手续费这两种方式来计算,需求里有一个规则是当其中一个没有值时,要根据有值的那个将其反算出来。这个算法前几天被QA测试发现,提交的交易单的贴息金额会存在几分钱的差异。同事将算法改造完毕后,再测试后并未复现这样的问题。但,这并不代表就彻底fix了。因此,我写了个testcase,批量随机生成一些测试案例,模拟穷举客户的输入,这其中,在比较两次计算结果时,就用到了md5,如果两次结果的md5不一样,那就证明算法依然存在问题。最后,经运行testcase,还真发现算法的不足,再次改正迭代测试后,bug不再复现。

    import com.emaxcard.codec.MD5Util;
    import com.emaxcard.rpc.payment.model.FeeCalculateReq;
    import com.emaxcard.rpc.payment.model.FeeCalculateRes;
    import org.apache.commons.lang.math.RandomUtils;
    import org.joda.time.DateTime;
    
    import java.math.BigDecimal;
    import java.math.RoundingMode;
    
    public class FeeCalculateServiceImplTest {
    
        //    @Test
    //    public void feeCalculate() {
        public static void main(String[] args) throws Exception {
    
            for (int i = 1; i < 10000; i++) {
                System.out.println(i);
                int rand = RandomUtils.nextInt(10);
                BigDecimal divide = BigDecimal.valueOf(i).divide(BigDecimal.valueOf(rand == 0 ? Math.PI : rand), 12, RoundingMode.HALF_UP);
                BigDecimal draftAmt = BigDecimal.valueOf(500000).add(divide).setScale(0, RoundingMode.HALF_UP);
                BigDecimal unitAmt = BigDecimal.valueOf(RandomUtils.nextInt(10)).add(divide).setScale(2, RoundingMode.HALF_UP);
                BigDecimal saleRate = divide.divide(BigDecimal.valueOf(100)).setScale(6, RoundingMode.HALF_UP);
                calculateFee(draftAmt,unitAmt, saleRate);
            }
        }
    
        static void calculateFee(BigDecimal draftAmt, BigDecimal unitAmt, BigDecimal saleRate) throws Exception {
            if (null == unitAmt) {
                unitAmt = BigDecimal.ZERO;
            }
            if (null == saleRate) {
                saleRate = BigDecimal.ZERO;
            }
            FeeCalculateReq feeCalculateReq = new FeeCalculateReq();
            feeCalculateReq.setDraftAmt(draftAmt.toPlainString());
            feeCalculateReq.setUnitAmt(unitAmt.toPlainString());
            feeCalculateReq.setSaleRate(saleRate.toPlainString());
            feeCalculateReq.setDraftEndDate(DateTime.now().plusYears(1).toString("yyyy-MM-dd"));
    
            FeeCalculateRes feeCalculateRes = FeeCalculateServiceImpl.calculateFee(feeCalculateReq);
            String s1 = MD5Util.md5(feeCalculateRes.toString());
    
            feeCalculateReq.setUnitAmt(feeCalculateRes.getUnitAmt());
            feeCalculateReq.setSaleRate(feeCalculateRes.getSaleRate());
            feeCalculateRes = FeeCalculateServiceImpl.calculateFee(feeCalculateReq);
            String s2 = MD5Util.md5(feeCalculateRes.toString());
    
            boolean equals = s1.equals(s2);
            if (equals == false) {
                throw new Exception("bad luck!!!!");
            }
        }
    }
    View Code

     

    谁的手总紧紧牵住我的手
    不回头在人群沙漠中漂泊
    你别用含着泪的眼睛看我
    听蝉声沉落
    请抬头今宵露重

    是谁用带露的草叶医治我
    愿共我顶风暴泥泞中跋涉
    是谁说经过的路都是必需 磨难尽收获
    山云做幕攀岩观火

    请由我引吭高歌 面迎啊海上风
    在世界之外 在时间之中 无问西东
    就奋身做个英雄 不枉那青春勇
    愿心之自由共天地俊秀
    有情有梦

    是谁用带露的草叶医治我
    愿共我顶风暴泥泞中跋涉
    是谁说经过的路都是必需 磨难尽收获
    山云做幕攀岩观火

    请由我引吭高歌 面迎啊海上风
    在世界之外 在时间之中 无问西东
    就奋身做个英雄 不枉那青春勇
    愿心之自由共天地俊秀
    有情有梦

  • 相关阅读:
    在DataGrid中创建一个点击列名时的弹出式窗口
    利用自定义事件实现不同窗体间的通讯 C#篇
    用javascript实现禁用鼠标右键
    刷新页面时,防止滚动条上滚
    web服务编程
    数据库链接Connection和DataReader的关闭
    .NET的WEB商业应用架构所要解决的若干
    zblog屏蔽分类文章
    过年随想
    mysql数据库文件的真实的物理存储位置
  • 原文地址:https://www.cnblogs.com/buguge/p/11206046.html
Copyright © 2011-2022 走看看