• 接口签名规则及Java代码demo实现


    接口签名规则及Java代码demo实现

    签名规则
    签名生成的通用步骤如下:
    第一步,设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。
    特别注意以下重要规则:
    ◆ 参数名ASCII码从小到大排序(字典序);
    ◆ 如果参数的值为空不参与签名;
    ◆ 参数名区分大小写;
    ◆ 验证接口调用传送的sign参数不参与签名,将生成的签名与该sign值作校验。
    第二步,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。 注意:密钥的长度为32个字节。

    1.导入jar
    implementation("commons-beanutils:commons-beanutils:1.9.3")

    2.MD5工具类

    import java.security.MessageDigest;
    
    
    public class MD5 {
        private final static String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "7",
                "8", "9", "a", "b", "c", "d", "e", "f"};
    
        /**
         * 转换字节数组为16进制字串
         * @param b 字节数组
         * @return 16进制字串
         */
        public static String byteArrayToHexString(byte[] b) {
            StringBuilder resultSb = new StringBuilder();
            for (byte aB : b) {
                resultSb.append(byteToHexString(aB));
            }
            return resultSb.toString();
        }
    
        /**
         * 转换byte到16进制
         * @param b 要转换的byte
         * @return 16进制格式
         */
        private static String byteToHexString(byte b) {
            int n = b;
            if (n < 0) {
                n = 256 + n;
            }
            int d1 = n / 16;
            int d2 = n % 16;
            return hexDigits[d1] + hexDigits[d2];
        }
    
        /**
         * MD5编码
         * @param origin 原始字符串
         * @return 经过MD5加密之后的结果
         */
        public static String MD5Encode(String origin) {
            String resultString = null;
            try {
                resultString = origin;
                MessageDigest md = MessageDigest.getInstance("MD5");
                resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
            } catch (Exception e) {
                e.printStackTrace();
            }
            return resultString;
        }
    
    }


    3.实体类

    import java.util.List;
    
    
    public class UploadReqVO {
        //手机号
        private String mobile;
        //姓名
        private String realname;
        //身份证号
        private String idno;
        //字符数组
        private List<String> testBase64Str;
        private String sign;
    
        public String getMobile() {
            return mobile;
        }
    
        public void setMobile(String mobile) {
            this.mobile = mobile;
        }
    
        public String getRealname() {
            return realname;
        }
    
        public void setRealname(String realname) {
            this.realname = realname;
        }
    
        public String getIdno() {
            return idno;
        }
    
        public void setIdno(String idno) {
            this.idno = idno;
        }
    
        public List<String> getTestBase64Str() {
            return testBase64Str;
        }
    
        public void setTestBase64Str(List<String> testBase64Str) {
            this.testBase64Str = testBase64Str;
        }
    
        public String getSign() {
            return sign;
        }
    
        public void setSign(String sign) {
            this.sign = sign;
        }
    }

    4.签名类及测试类

    import com.alibaba.fastjson.JSON;
    import org.apache.commons.beanutils.BeanUtils;
    import org.apache.tomcat.util.security.MD5Encoder;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.util.StringUtils;
    import org.xml.sax.SAXException;
    
    import javax.xml.parsers.ParserConfigurationException;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.nio.charset.StandardCharsets;
    import java.security.MessageDigest;
    import java.util.*;
    
    
    public class Signature {
        private static Logger log = LoggerFactory.getLogger(Signature.class);
    
        /**
         * 对象入参,对象转MAP
         * @param object
         * @return
         * @throws Exception
         */
        public static String getSignNew(Object object,String key) throws Exception {
            Map<String, String> map = BeanUtils.describe(object);
            return getSignNew(map,key);
        }
    
        public static String getSignNewArray(UploadReqVO object, String key) throws Exception {
            Map<String, String> map = BeanUtils.describe(object);
            //覆盖
            map.put("testBase64Str", JSON.toJSONString(object.getTestBase64Str()).replace("\"",""));
            return getSignNew(map,key);
        }
    
    
    
        /**
         * 签名
         * @param map
         * @return
         */
        private static String getSignNew(Map<String,String> map,String key) throws Exception{
            ArrayList<String> list = new ArrayList<String>();
            for(Map.Entry<String,String> entry:map.entrySet()){
                if(entry.getValue() != null && !StringUtils.isEmpty(entry.getValue().toString()) && !"null".equals(entry.getValue())
                && !"class".equals(entry.getKey())){  //空字符串 entry.getValue()!=""){
                    list.add(entry.getKey() + "=" + entry.getValue() + "&");
                }
            }
            int size = list.size();
            String [] arrayToSort = list.toArray(new String[size]);
    //        Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);  //忽略大小写
            Arrays.sort(arrayToSort);
            StringBuilder sb = new StringBuilder();
            for(int i = 0; i < size; i ++) {
                sb.append(arrayToSort[i]);
            }
            String result = sb.toString();
            //过滤最后一个字符串&
            int lastIdx = result.lastIndexOf("&");
            result = result.substring(0,lastIdx);
    //        result += "key=" + key;   //去掉key= 字符串
            result +=  key;   //key直接拼接在后面
    
            try{
                log.info("Sign Before MD5:"+ result);
                result = MD5.MD5Encode(result);
                log.info("Sign Result:" + result);
            }catch (Exception e) {
                e.printStackTrace();
            }
    
            return result;
        }
    
    
    
        public static boolean checkIsSignValidFromResyponseStringObject(Object object,String key) throws Exception {
            Map<String, String> map = org.apache.commons.beanutils.BeanUtils.describe(object);
            return checkIsSignValidFromResponseString(map,key);
        }
    
        public static boolean checkIsSignValidFromResponseStringArray(UploadReqVO object, String key) throws Exception {
            Map<String, String> map = BeanUtils.describe(object);
            //覆盖
            map.put("testBase64Str", JSON.toJSONString(object.getTestBase64Str()).replace("\"",""));
            return checkIsSignValidFromResponseString(map,key);
        }
    
    
        /**
         * object转换为map  验证签名
         * @param map
         * @return
         * @throws ParserConfigurationException
         * @throws IOException
         * @throws SAXException
         * @throws IllegalAccessException 
         */
        private static boolean checkIsSignValidFromResponseString(Map<String,String> map,String key) throws Exception {
            String signFromAPIResponse = null;
            if(map.get("sign")!=null){
                signFromAPIResponse = map.get("sign").toString();
            }
    
            if(signFromAPIResponse=="" || signFromAPIResponse == null){
                log.info("signFromAPIResponse报空");
                return false;
            }
            
            //清掉返回数据对象里面的Sign数据(不能把这个数据也加进去进行签名),然后用签名算法进行签名
            map.put("sign","");
            map.put("class","");
            //将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较
            //重新签名
            log.info("签名前的map="+map);
            String signForAPIResponse = Signature.getSignNew(map,key);
            log.info("签名后的字符串="+signForAPIResponse);
            
            if(!signForAPIResponse.equals(signFromAPIResponse)){
                //签名验不过,表示这个API返回的数据有可能已经被篡改了
                log.info("匹配不一致");
                return false;
            }
            return true;
        }
    
        
        public static void main(String[] args) {
            String key = "testkey123testkey123testkey12345";
    
            try {
                UploadReqVO uploadReqVo = new UploadReqVO();
    
                List<String> pic1List = new ArrayList<String>();
                String pic5 = Base64.getEncoder().encodeToString("测试字符串1".getBytes(StandardCharsets.UTF_8));
                pic1List.add(pic5);
                String pic6 = Base64.getEncoder().encodeToString("测试字符串2".getBytes(StandardCharsets.UTF_8));
                pic1List.add(pic6);
                uploadReqVo.setTestBase64Str(pic1List);
    
                uploadReqVo.setIdno("465601200810081204");
                uploadReqVo.setRealname("测试员");
                uploadReqVo.setMobile("19945558899");
                uploadReqVo.setSign("");
                //数组方法
                String signStr3 = Signature.getSignNewArray(uploadReqVo,key);
                //非数组方法
    //            String signStr3 = Signature.getSignNew(uploadReqVo,key);
                System.out.println("(上传图片)签名字符串:" + signStr3);
                uploadReqVo.setSign(signStr3);
                System.out.println("(上传图片)参数json=" + JSON.toJSONString(uploadReqVo));
    
                //验证签名
                //数组方法
                boolean flag3 = Signature.checkIsSignValidFromResponseStringArray(uploadReqVo,key);
                //非数组方法
    //            boolean flag3 = Signature.checkIsSignValidFromResyponseStringObject(uploadReqVo,key);
                System.out.println("(上传图片)验证签名是否一致="+flag3);
    
            } catch (Exception e) {
                e.printStackTrace();
            }
            
        }
    }
    打印输出日志对比:	
    //数组方式
    16:56:50.014 [main] INFO com.example.utils.Signature - Sign Before MD5:idno=465601200810081204&mobile=19945558899&realname=测试员&testBase64Str=[5rWL6K+V5a2X56ym5LiyMQ==,5rWL6K+V5a2X56ym5LiyMg==]testkey123testkey123testkey12345
    16:56:50.061 [main] INFO com.example.utils.Signature - Sign Result:61867a7f32594eec1967fcddea8d96c3
    (上传图片)签名字符串:61867a7f32594eec1967fcddea8d96c3
    (上传图片)参数json={"idno":"465601200810081204","mobile":"19945558899","realname":"测试员","sign":"61867a7f32594eec1967fcddea8d96c3","testBase64Str":["5rWL6K+V5a2X56ym5LiyMQ==","5rWL6K+V5a2X56ym5LiyMg=="]}
    
    16:56:50.089 [main] INFO com.example.utils.Signature - 签名前的map={testBase64Str=[5rWL6K+V5a2X56ym5LiyMQ==,5rWL6K+V5a2X56ym5LiyMg==], mobile=19945558899, sign=, class=, idno=465601200810081204, realname=测试员}
    16:56:50.089 [main] INFO com.example.utils.Signature - Sign Before MD5:idno=465601200810081204&mobile=19945558899&realname=测试员&testBase64Str=[5rWL6K+V5a2X56ym5LiyMQ==,5rWL6K+V5a2X56ym5LiyMg==]testkey123testkey123testkey12345
    16:56:50.089 [main] INFO com.example.utils.Signature - Sign Result:61867a7f32594eec1967fcddea8d96c3
    16:56:50.089 [main] INFO com.example.utils.Signature - 签名后的字符串=61867a7f32594eec1967fcddea8d96c3
    (上传图片)验证签名是否一致=true
    	
    
    //非数组方法,弊端是:testBase64Str构建签名字符串的时候,默认取数组的第一个字符,而不是[]结构的全部数据。
    16:59:15.692 [main] INFO com.example.utils.Signature - Sign Before MD5:idno=465601200810081204&mobile=19945558899&realname=测试员&testBase64Str=5rWL6K+V5a2X56ym5LiyMQ==testkey123testkey123testkey12345
    16:59:15.697 [main] INFO com.example.utils.Signature - Sign Result:bfc9246143246f1852b4a29732aabcf6
    (上传图片)签名字符串:bfc9246143246f1852b4a29732aabcf6
    (上传图片)参数json={"idno":"465601200810081204","mobile":"19945558899","realname":"测试员","sign":"bfc9246143246f1852b4a29732aabcf6","testBase64Str":["5rWL6K+V5a2X56ym5LiyMQ==","5rWL6K+V5a2X56ym5LiyMg=="]}
    
    16:59:15.775 [main] INFO com.example.utils.Signature - 签名前的map={testBase64Str=5rWL6K+V5a2X56ym5LiyMQ==, mobile=19945558899, sign=, class=, idno=465601200810081204, realname=测试员}
    16:59:15.775 [main] INFO com.example.utils.Signature - Sign Before MD5:idno=465601200810081204&mobile=19945558899&realname=测试员&testBase64Str=5rWL6K+V5a2X56ym5LiyMQ==testkey123testkey123testkey12345
    16:59:15.775 [main] INFO com.example.utils.Signature - Sign Result:bfc9246143246f1852b4a29732aabcf6
    16:59:15.775 [main] INFO com.example.utils.Signature - 签名后的字符串=bfc9246143246f1852b4a29732aabcf6
    (上传图片)验证签名是否一致=true
  • 相关阅读:
    注意力机制的命名实体识别
    tfidf_CountVectorizer 与 TfidfTransformer 保存和测试
    转载:UML学习(三)-----序列图(silent)
    转载:UML学习(二)-----类图(silent)
    转载:UML学习(一)-----用例图 (silent)
    转载:Practical UML™: A Hands-On Introduction for Developers
    转载:gc的概念,如果A和B对象循环引用,是否可以被GC?
    转载:Java高并发,如何解决,什么方式解决
    转载:消息队列应用场景
    转载:详解Java 自动装箱与拆箱的实现原理
  • 原文地址:https://www.cnblogs.com/oktokeep/p/16769564.html
Copyright © 2020-2023  润新知