欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JAVA實現(xiàn)sm3加密簽名以及防止重復(fù)攻擊

 更新時間:2023年10月31日 15:59:41   作者:小白的碼BUG之路  
這篇文章主要給大家介紹了關(guān)于JAVA實現(xiàn)sm3加密簽名以及防止重復(fù)攻擊的相關(guān)資料,SM3是簽名算法,和MD5一樣(對于應(yīng)用層來說),SM4是對稱加密算法,和AES一樣(對于應(yīng)用層來說),需要的朋友可以參考下

背景:

后端開發(fā)基本都遇到過使用簽名校驗的情況,簽名的作用是為了防止請求數(shù)據(jù)被別人截取篡改重新請求。

為什么簽名驗證可以防止請求數(shù)據(jù)被篡改,因為一般簽名的規(guī)則就是,你的所有請求參數(shù),按照約定好的格式進(jìn)行拼接,后面得到一個拼接后的字符串,這個字符串后面再加上一個雙方私下確認(rèn)后的簽名秘鑰(固定),最后組合成一個待簽名字符串,用這個字符串進(jìn)行sm3加密。sm3加密是個不可逆的加密(理論上),請求方把這個加密后面簽名字段放到請求頭中,服務(wù)提供方本地根據(jù)相同的方法進(jìn)行組合簽名字符串和加密,然后對面本地加密的簽名和請求頭中的簽名是否相同,相同則認(rèn)為這個數(shù)據(jù)是可靠的,未被篡改過的(加密前數(shù)據(jù)不同,加密后的簽名肯定不同)。

PS:簽名只能驗證防止請求數(shù)據(jù)被篡改,并不能說你把數(shù)據(jù)加密讓別人看不見,只要是互聯(lián)網(wǎng)上傳輸,數(shù)據(jù)就可能被別人獲取到

簽名優(yōu)化:

請求頭加上時間戳,代碼上驗證請求的時間戳和當(dāng)前時間戳?xí)r間差距,差距過大就拒絕請求(比如只接受一分鐘內(nèi)請求),同時把這個時間戳加到簽名字符串去,這樣簽名的數(shù)據(jù)內(nèi)容就包含時間戳(可以防止別人用同一份簽名發(fā)起重復(fù)請求攻擊,因為時間戳是一直在變的,簽名中的時間戳和請求報文頭的時間戳不同,驗證簽名就不會通過),這樣就可以做到即使有人獲取到了某個請求的參數(shù)和簽名,以此發(fā)起多次重復(fù)請求攻擊,這種攻擊最多只有一分鐘的有效期。

代碼:

package com.dw.task.utils;

import cn.hutool.crypto.SmUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.util.Objects;

@Slf4j
@Component
public class Sm3UtilHua {

    private static Integer httpCheckSignTimeOut = 1;//請求簽名有效時間 默認(rèn)1分鐘

    /**
     * 獲取實體類拼成的加密字段
     * @param checkSign  前端傳入的簽名(從請求報文頭獲?。?
     * @param signModel  查詢的DTO模型類
     * @param privateKey  簽名加密私鑰
     * @param timestamp  時間戳(從請求報文頭獲?。?
     * @return  比對結(jié)果
     */
    public boolean checkSign(String checkSign , Object signModel , String privateKey,Long timestamp) throws Exception {
        Long thisTime = System.currentTimeMillis() - timestamp;
        Integer checkSignTimeOut = httpCheckSignTimeOut;
        if (!(Objects.isNull(checkSignTimeOut) || checkSignTimeOut.intValue() == 0)){
            //時間為0或者未配置簽名超時時間,默認(rèn)不驗證時間戳
            if(thisTime >= 60*1000*checkSignTimeOut || thisTime<=0){
                //checkSignTimeOut分鐘內(nèi)的時間戳才處理
                log.error("時間戳異常,非"+checkSignTimeOut+"分鐘內(nèi)請求,當(dāng)前時間戳:"+System.currentTimeMillis());
                return false;
            }
        }
        String signValue = getSignValue(signModel) + "&timestamp=" + timestamp +"&privateKey=" +privateKey;
        String sign = getSign(signValue);
        log.info("【本地加密后 sm3 簽名】" + sign);//生產(chǎn)上建議注釋此行,防止泄露
        return sign.toUpperCase().equals(checkSign.toUpperCase())? true : false;
    }
    /**
     * 加密簽名
     * @param signValue  待加密簽名字符串
     * @return  加密后簽名字符串
     */
    public String getSign(String signValue){
        return SmUtil.sm3(signValue);
    }
    
    /**
     * 獲取實體類拼成的加密字段
     * @param classA  傳入?yún)?shù)實體類
     * @return  待加密字符串
     */
    public  String getSignValue(Object classA) {
        Field[] fs = classA.getClass().getDeclaredFields();//獲取所有屬性
        String[][] temp = new String[fs.length][2]; //用二維數(shù)組保存  參數(shù)名和參數(shù)值
        for (int i=0; i<fs.length;  i++) {
            fs[i].setAccessible(true);
            temp[i][0] = fs[i].getName().toLowerCase(); //獲取屬性名
            try {
                temp[i][1] = String.valueOf(fs[i].get(classA)) ;//把屬性值放進(jìn)數(shù)組
            }  catch (Exception e) {
                log.error("【簽名字段:"+fs[i].getName()+"添加失敗】");
            }
        }
        temp = doChooseSort(temp); //對參數(shù)實體類按照字母順序排續(xù)
        String result = "";
        for (int i = 0; i < temp.length; i++) {//按照簽名規(guī)則生成待加密字符串
            result = result + temp[i][0]+"="+temp[i][1]+"&";
        }
        result = result.substring(0, result.length()-1);//消除掉最后的“&”
        log.info("【簽名信息】{}" ,result);
        return result;
    }

    /**
     * 對二維數(shù)組里面的數(shù)據(jù)進(jìn)行選擇排序,按字段名按abcd順序排列
     * @param data 未按照字母順序排序的二維數(shù)組
     * @return
     */
    private  String[][] doChooseSort(String[][] data) {//排序方式為選擇排序
        String[][] temp = new String[data.length][2];
        temp = data;
        int n = temp.length;
        for (int i = 0; i < n-1; i++) {
            int k = i;// 初始化最小值的小標(biāo)
            for (int j = i+1; j<n; j++) {
                if (temp[k][0].compareTo(temp[j][0]) > 0) {    //下標(biāo)k字段名大于當(dāng)前字段名
                    k = j;// 修改最大值的小標(biāo)
                }
            }
            // 將最小值放到排序序列末尾
            if (k > i) {  //用相加相減法交換data[i] 和 data[k]
                String tempValue ;
                tempValue = temp[k][0];
                temp[k][0] = temp[i][0];
                temp[i][0] = tempValue;
                tempValue = temp[k][1];
                temp[k][1] = temp[i][1];
                temp[i][1] = tempValue;
            }
        }
        return temp;
    }

}

測試代碼:

    public static void main(String[] args) throws Exception {
        //模擬請求參數(shù)
        QueryDTO dto = new QueryDTO();
        dto.setName("張三");
        dto.setAge(18);
        dto.setIdCard("666666666666666");
        //模擬請求報文頭時間戳
        Long nowTime = System.currentTimeMillis();
        //簽名加密私鑰(不要在互聯(lián)網(wǎng)上傳輸,調(diào)用方和提供方私下物理確認(rèn)和設(shè)置)
        String privateKey = "48d95af20fa1bc438db42e280085707b60841c";

        Sm3UtilHua sm3UtilHua = new Sm3UtilHua();

        System.out.println("1:請求簽名錯誤的案例");
        //模擬請求錯誤的簽名
        String errorSign = "2222222222222222222";
        System.out.println("驗簽校驗結(jié)果:" + sm3UtilHua.checkSign(errorSign,dto, privateKey,nowTime));
        System.out.println();

        System.out.println("2:請求簽名正確的案例");
        String signValue = sm3UtilHua.getSignValue(dto) + "&timestamp=" + nowTime +"&privateKey=" +privateKey;//復(fù)制上面1請求的加密后簽名
        String trueSign = sm3UtilHua.getSign(signValue);
        System.out.println("驗簽校驗結(jié)果:" + sm3UtilHua.checkSign(trueSign,dto, privateKey,nowTime));
        System.out.println();

        System.out.println("3:請求簽名正確,但是時間非一分鐘內(nèi)請求的案例");
        Long oldTime = 1688036145560L;//這個時間戳大概是2023年06月29號的時間戳,所以必不是當(dāng)前一分鐘內(nèi)時間
        System.out.println("驗簽校驗結(jié)果:" + sm3UtilHua.checkSign(trueSign,dto, privateKey,oldTime));

    }

測試結(jié)果:

下面的簽名信息打印是不完整的,沒有拼接時間戳和簽名私鑰,也是防止打印到日志然后泄露

1:請求簽名錯誤的案例
10:42:11.506 [main] ERROR com.dw.task.utils.Sm3UtilHua - 時間戳異常,非1分鐘內(nèi)請求,當(dāng)前時間戳:1688092931498
驗簽校驗結(jié)果:false

2:請求簽名正確的案例
10:42:11.522 [main] INFO com.dw.task.utils.Sm3UtilHua - 【簽名信息】age=18&idcard=666666666666666&name=張三
10:42:12.358 [main] INFO com.dw.task.utils.Sm3UtilHua - 【簽名信息】age=18&idcard=666666666666666&name=張三
10:42:12.359 [main] INFO com.dw.task.utils.Sm3UtilHua - 【本地加密后 sm3 簽名】e60bf8ea2453f44a4a6d3b43f55399c2ce09a6f5b4be68378506d95d2d6f4491
驗簽校驗結(jié)果:true

3:請求簽名正確,但是時間非一分鐘內(nèi)請求的案例
10:42:12.359 [main] ERROR com.dw.task.utils.Sm3UtilHua - 時間戳異常,非1分鐘內(nèi)請求,當(dāng)前時間戳:1688092932359
驗簽校驗結(jié)果:false

引申優(yōu)化:

上面的代碼已經(jīng)可以防止一定程度的重復(fù)請求攻擊,但是一分鐘內(nèi)的重復(fù)請求攻擊還是無法防止的,如果別人截取請求信息后一分鐘內(nèi)發(fā)起大量重復(fù)請求的話,還是會通過簽名并且穿透到業(yè)務(wù)層,對此呢如果要再優(yōu)化,就可以把每次驗證成功的簽名放到redis緩存中,數(shù)據(jù)有效期是1分鐘。這樣每次在驗簽之前先查詢redis緩存中是否有相同的簽名,有即代表這個請求是重復(fù)請求,直接攔截

 思路就是這樣,代碼就不寫了

總結(jié)

到此這篇關(guān)于JAVA實現(xiàn)sm3加密簽名以及防止重復(fù)攻擊的文章就介紹到這了,更多相關(guān)JAVA sm3加密簽名內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:

相關(guān)文章

  • idea如何為java程序添加啟動參數(shù)

    idea如何為java程序添加啟動參數(shù)

    文章介紹了如何在Java程序中添加啟動參數(shù),包括program arguments、VM arguments和Environment variables,并解釋了如何在代碼中使用System類獲取這些參數(shù)
    2025-01-01
  • Java利用openoffice將doc、docx轉(zhuǎn)為pdf實例代碼

    Java利用openoffice將doc、docx轉(zhuǎn)為pdf實例代碼

    這篇文章主要介紹了Java利用openoffice將doc、docx轉(zhuǎn)為pdf實例代碼,分享了相關(guān)代碼示例,小編覺得還是挺不錯的,具有一定借鑒價值,需要的朋友可以參考下
    2018-01-01
  • mybatis-plus中配置日志信息方式

    mybatis-plus中配置日志信息方式

    這篇文章主要介紹了mybatis-plus中配置日志信息方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • java實現(xiàn)高效的枚舉元素集合示例

    java實現(xiàn)高效的枚舉元素集合示例

    Set是Java集合類的重要組成部分,它用來存儲不能重復(fù)的對象。枚舉類型也要求其枚舉元素各不相同。看起來枚舉類型和集合是很相似的。然而枚舉類型中的元素不能隨意的增加、刪除,作為集合而言,枚舉類型非常不實用。EnumSet是專門為enum實現(xiàn)的集合類,本實例將演示其用法
    2014-03-03
  • 一文解讀java.nio.ByteBuffer

    一文解讀java.nio.ByteBuffer

    這篇文章主要介紹了java.nio.ByteBuffer的用法解讀,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • Spring中BeanFactory解析bean詳解

    Spring中BeanFactory解析bean詳解

    本篇文章主要介紹了Spring中BeanFactory解析bean詳解 ,詳細(xì)的介紹了使用BeanFactory對bean進(jìn)行解析的實例,有興趣的可以了解一下。
    2017-04-04
  • SpringBoot輕松整合MongoDB的全過程記錄

    SpringBoot輕松整合MongoDB的全過程記錄

    這篇文章主要給大家介紹了關(guān)于SpringBoot輕松整合MongoDB的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • Java Apollo環(huán)境搭建以及集成SpringBoot案例詳解

    Java Apollo環(huán)境搭建以及集成SpringBoot案例詳解

    這篇文章主要介紹了Java Apollo環(huán)境搭建以及集成SpringBoot案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • MybatisPlus中QueryWrapper常用方法總結(jié)

    MybatisPlus中QueryWrapper常用方法總結(jié)

    MyBatis-Plus是一個Mybatis增強版工具,在MyBatis上擴(kuò)充了其他功能沒有改變其基本功能,為了簡化開發(fā)提交效率而存在,queryWrapper是mybatis plus中實現(xiàn)查詢的對象封裝操作類,本文就給大家總結(jié)了MybatisPlus中QueryWrapper的常用方法,需要的朋友可以參考下
    2023-07-07
  • 一分鐘入門Java Spring Boot徹底解決SSM配置問題

    一分鐘入門Java Spring Boot徹底解決SSM配置問題

    Spring Boot是由Pivotal團(tuán)隊提供的全新框架,其設(shè)計目的是用來簡化新Spring應(yīng)用的初始搭建以及開發(fā)過程。該框架使用了特定的方式來進(jìn)行配置,從而使開發(fā)人員不再需要定義樣板化的配置。通過這種方式,Spring Boot致力于在蓬勃發(fā)展的快速應(yīng)用開發(fā)領(lǐng)域成為領(lǐng)導(dǎo)者
    2021-10-10

最新評論