zoukankan      html  css  js  c++  java
  • MD5签名生成,字典排序,实际生产

    1.背景

    作用:防止请求参数篡改,限制请求时效性;

    常用方式:md5签名

    关键:签名Key

    常用签名原串排列:字母顺序、key1=value1&key2=value2....key (注意:签名规则是双方协商好,不一定是这个规则,这里只是以常规规则举例)

    常用排序实现方式,利用TreeMap,进行排序,或者Arrays.sort先对key排序,然后根据key的顺序取值.

    具体实现方案:aop统一接口签名

    统一签名aop代码如下:

    package com.ldp.user.common.aop;
    
    import cn.hutool.core.util.StrUtil;
    import cn.hutool.crypto.SecureUtil;
    import cn.hutool.extra.servlet.ServletUtil;
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import com.ldp.user.common.exception.ParamException;
    import lombok.extern.slf4j.Slf4j;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.servlet.http.HttpServletRequest;
    import java.util.Map;
    import java.util.TreeMap;
    
    /**
     * @Copyright (C) XXXXX技有限公司
     * @Author: lidongping
     * @Date: 2020-12-16 18:36
     * @Description: <p>
     * 接口签名检查aop
     * </p>
     */
    @Slf4j
    @Aspect
    @Component
    @SuppressWarnings("all")
    public class SignAspect {
        private static final String MD5_KEY = "123456";
    
        /**
         * 切入控制层
         */
        @Pointcut("execution(* com.ldp.user.controller.*Controller.*(..))")
        public void controllerAspect() {
        }
    
        /**
         * 切入控制层基类
         */
        @Pointcut("execution(* com.ldp.user.common.base.BaseController.*(..))")
        public void baseControllerAspect() {
        }
    
        @Before("controllerAspect() || baseControllerAspect()")
        private void before(JoinPoint joinPoint) {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            // json格式请求的参数
            String params = ServletUtil.getBody(request);
            if (StrUtil.isEmpty(params)) {
                // key-value请求参数
                Map<String, String> paramMap = ServletUtil.getParamMap(request);
                params = JSONObject.toJSONString(paramMap);
            }
            // 签名检查
            checkSign(params);
        }
    
        /**
         * 签名验证的作用: 防止参数篡改
         * <p>
         * 签名规则:
         * 1. 除sign字段外,所有(有值的字段)字段按照字母顺序的"key1=value1&key2=value2&.....keyn=valuen"格式排序,等到str
         * 2. str拼接上MD5_KEY,得到strMd5签名原串
         * 3. 对strMd5字符串签名,得到strSign签名串
         * <p>
         * 案例
         * 请求原数据: {  "buyAccount": "wx001",  "orderNo": "LDP003",  "price": 6990,  "productName": "苹果手机",  "sign":"06282c01753c2d0b6d45491798b29de5"}
         * 签名key: 123456
         * 签名原串: buyAccount=wx001&orderNo=LDP003&price=6990&productName=苹果手机123456
         * 签名值: 06282c01753c2d0b6d45491798b29de5
         * 用户签名值: 06282c01753c2d0b6d45491798b29de5
         * <p>
         * 签名值可以在线生成校验
         * 在线md5签名链接: https://md5jiami.51240.com/
         *
         * @param params
         * @throws ParamException
         */
        public void checkSign(String params) throws ParamException {
            JSONObject jsonObject = JSON.parseObject(params);
            // 有参数才检查签名
            if (jsonObject == null || jsonObject.size() == 0) {
                return;
            }
            // 准备sign
            String sign = jsonObject.getString("sign");
            if (StrUtil.isEmpty(sign)) {
                throw new ParamException("签名值为空");
            }
            // 删除json中的sign
            jsonObject.remove("sign");
            // 准备签名字符串
            StringBuilder sb = new StringBuilder();
            // TreeMap能够根据key按照字典排序
            Map<String, Object> map = new TreeMap(jsonObject);
            // 遍历排序的字典,并拼接"key1=value1&key2=value2&.....keyn=valuen"格式
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                Object value = entry.getValue();
                String key = entry.getKey();
                if (value == null || StrUtil.isEmpty(value.toString())) {
                    continue;
                }
                if ("".equals(sb.toString())) {
                    sb.append(key + "=").append(value.toString());
                } else {
                    sb.append("&" + key + "=").append(value.toString());
                }
            }
            String strMd5 = sb.toString() + MD5_KEY;
            String md5 = SecureUtil.md5(strMd5);
            log.info("请求原数据:" + params);
            log.info("签名key:" + MD5_KEY);
            log.info("签名原串:" + strMd5);
            log.info("签名值:" + md5);
            log.info("用户签名值:" + sign);
            String lowerCase = sign.toLowerCase();
            if (!lowerCase.equals(md5)) {
                throw new ParamException("签名失败");
            }
            log.info("签名成功");
        }
    }
    View Code

    面试点:我们经常说面试时知识点要结合实际生产,那么,

    如果面试官问了你关于map相关的知识点后,你是不是可以举例说比如在签名的时候我们可以使用TreeMap对签名原串进行排序的实际生产应用案例;

    如果问到关于aop的技术点的时候,你可以说比如api接口统一签名使用aop实现的案例.....

    这样可以大大提升你在面试官心目中的实践能力.....

    如果你还是对这些不是很理解,可以看讲解视频,里面会详细介绍签名的原理、为什么可以防止请求参数篡改、具体实现方案等。

    2.MD5简介

    MD5加密全程是Message-Digest Algoorithm 5(信息-摘要算法),它对信息进行摘要采集,再通过一定的位运算,最终获取加密后的MD5字符串。

    例如我们要加密一篇文章,那么我们会随机从每段话或者每行中获取一个字,把这些字统计出来后,再通过一定的运算获得一个固定长度的MD5加密后信息。

    因此,其很难被逆向破解。

    3.MD5算法具有以下特点

    1、压缩性:任意长度的数据,算出的MD5值长度都是固定的。

    2、容易计算:从原数据计算出MD5值很容易。

    3、抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。

    4、弱抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。

    5、强抗碰撞:想找到两个不同的数据,使它们具有相同的MD5值,是非常困难的。

    MD5的作用是让大容量信息在用数字签名软件签署私人密钥前被”压缩”成一种保密的格式(就是把一个任意长度的字节串变换成一定长的十六进制数字串)。

    除了MD5以外,其中比较有名的还有sha-1、RIPEMD以及Haval等。

    4.MD5是不可逆性

    在讲解MD5签名原理之前,必须明白MD5是不可逆的,也就是不可破解的,

    当然可能稍有了解的人会说MD5可以破解,

    网上也有一些强大的数据库会把各种数据组合的MD5收集起来形成强大的数据库,

    在数据库搜索可能一些简单的数据会马上找出某些组合对应的MD5值,

    但咱说的MD5签名生成是最少32位的数值MD5加密,

    所以这个破解的可能性是非常非常非常小的。所以这还是比较安全的。

    5.原理

    原理很简单,请求方对请求数据按一定的规则排序,加上appkey码一起通过MD5加密生成签名,

    然后把请求数据和签名发给服务方,服务方拿到数据后,去掉无用的数据(值为空不一般不参与签名,或其他约定好了不签名的字段),

    通过appid找到请求方的appkey,然后按同样的规则处理数据,并加上appkey通过MD5加密也生成签名,然

    后和请求方生成的签名去对比,如果值一样,签名验证通过。

    6.需要约定的数据

    服务商一般会给你一个appid,appkey;

    同时这两个参数服务商也会保存,这两个形成了你的唯一标识。

    appid通过网络传输,而appkey是不在网络上进行传输的,只在生成签名时使用,所以安全性还是比较高的。

    7.MD5生成签名的流程

    1. 除去空值和签名值或其他约定不签名的值

    2. 把参数字典排序,按照“参数=参数值”的模式用“&”字符拼接成字符串

    3. 加上appkey值,对形成的数据进行MD5加密,生成签名

    8.验证签名

    1.验证签名时与生成签名时逻辑一样,将生成的签名进行比对

    2.值得注意的是,很多签名带了时间戳参数,请求参数有时效性

    即:就是算一个正确的签名也只能在30秒以内有效,这样可以防止对签名的破解

    最后

    这只是常规的签名做法,具体的签名规则和签名方式很多时候由双方协定好就可以了

    但是建议大家在使用签名时,使用常规签名规则这样有很多好处......毕竟这是实际生产的经验.....也是很多人属性的规则,这样效率高得多

    完美!

  • 相关阅读:
    系统的访问
    tomcat 和 数据库的连接
    实体类编写规则
    webmagic 爬虫
    docker安装官方Redis镜像并启用密码认证
    解决Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. 问题
    Springboot配置druid报错Failed to bind properties under 'spring.datasource' to javax.sql.DataSource
    阿里云centos7.6搭建SVN远程仓库和Git远程仓库
    java 三大特性封装继承多态
    使用easyui tab需要注意的问题
  • 原文地址:https://www.cnblogs.com/newAndHui/p/14219697.html
Copyright © 2011-2022 走看看