最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

背景:

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

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

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

簽名優(yōu)化:

請求頭加上時(shí)間戳,代碼上驗(yàn)證請求的時(shí)間戳和當(dāng)前時(shí)間戳?xí)r間差距,差距過大就拒絕請求(比如只接受一分鐘內(nèi)請求),同時(shí)把這個(gè)時(shí)間戳加到簽名字符串去,這樣簽名的數(shù)據(jù)內(nèi)容就包含時(shí)間戳(可以防止別人用同一份簽名發(fā)起重復(fù)請求攻擊,因?yàn)闀r(shí)間戳是一直在變的,簽名中的時(shí)間戳和請求報(bào)文頭的時(shí)間戳不同,驗(yàn)證簽名就不會(huì)通過),這樣就可以做到即使有人獲取到了某個(gè)請求的參數(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;//請求簽名有效時(shí)間 默認(rèn)1分鐘

    /**
     * 獲取實(shí)體類拼成的加密字段
     * @param checkSign  前端傳入的簽名(從請求報(bào)文頭獲取)
     * @param signModel  查詢的DTO模型類
     * @param privateKey  簽名加密私鑰
     * @param timestamp  時(shí)間戳(從請求報(bào)文頭獲?。?
     * @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)){
            //時(shí)間為0或者未配置簽名超時(shí)時(shí)間,默認(rèn)不驗(yàn)證時(shí)間戳
            if(thisTime >= 60*1000*checkSignTimeOut || thisTime<=0){
                //checkSignTimeOut分鐘內(nèi)的時(shí)間戳才處理
                log.error("時(shí)間戳異常,非"+checkSignTimeOut+"分鐘內(nèi)請求,當(dāng)前時(shí)間戳:"+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);
    }
    
    /**
     * 獲取實(shí)體類拼成的加密字段
     * @param classA  傳入?yún)?shù)實(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ù)實(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");
        //模擬請求報(bào)文頭時(shí)間戳
        Long nowTime = System.currentTimeMillis();
        //簽名加密私鑰(不要在互聯(lián)網(wǎng)上傳輸,調(diào)用方和提供方私下物理確認(rèn)和設(shè)置)
        String privateKey = "48d95af20fa1bc438db42e280085707b60841c";

        Sm3UtilHua sm3UtilHua = new Sm3UtilHua();

        System.out.println("1:請求簽名錯(cuò)誤的案例");
        //模擬請求錯(cuò)誤的簽名
        String errorSign = "2222222222222222222";
        System.out.println("驗(yàn)簽校驗(yàn)結(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("驗(yàn)簽校驗(yàn)結(jié)果:" + sm3UtilHua.checkSign(trueSign,dto, privateKey,nowTime));
        System.out.println();

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

    }

測試結(jié)果:

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

1:請求簽名錯(cuò)誤的案例
10:42:11.506 [main] ERROR com.dw.task.utils.Sm3UtilHua - 時(shí)間戳異常,非1分鐘內(nèi)請求,當(dāng)前時(shí)間戳:1688092931498
驗(yàn)簽校驗(yàn)結(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
驗(yàn)簽校驗(yàn)結(jié)果:true

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

引申優(yōu)化:

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

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

總結(jié)

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

您可能感興趣的文章:

相關(guān)文章

  • selenium高效應(yīng)對Web頁面元素刷新的實(shí)例講解

    selenium高效應(yīng)對Web頁面元素刷新的實(shí)例講解

    今天小編就為大家分享一篇selenium高效應(yīng)對Web頁面元素刷新的實(shí)例講解,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-05-05
  • 詳解springboot集成websocket的兩種實(shí)現(xiàn)方式

    詳解springboot集成websocket的兩種實(shí)現(xiàn)方式

    這篇文章主要介紹了springboot集成websocket的兩種實(shí)現(xiàn)方式,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01
  • Java實(shí)現(xiàn)一個(gè)簡單的緩存方法

    Java實(shí)現(xiàn)一個(gè)簡單的緩存方法

    本篇文章主要介紹了Java實(shí)現(xiàn)一個(gè)簡單的緩存方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-04-04
  • Java對日期Date類進(jìn)行加減運(yùn)算、年份加減月份加減、時(shí)間差等等

    Java對日期Date類進(jìn)行加減運(yùn)算、年份加減月份加減、時(shí)間差等等

    這篇文章主要介紹了Java對日期Date類進(jìn)行加減運(yùn)算、年份加減月份加減、時(shí)間差等等,在網(wǎng)上查閱資料,加上自己總結(jié)的一些關(guān)于Date類的工具類
    2017-01-01
  • java中使用url進(jìn)行編碼和解碼

    java中使用url進(jìn)行編碼和解碼

    這篇文章主要介紹了java中使用url進(jìn)行編碼和解碼,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • springboot maven 項(xiàng)目打包jar 最后名稱自定義的教程

    springboot maven 項(xiàng)目打包jar 最后名稱自定義的教程

    這篇文章主要介紹了springboot maven 項(xiàng)目打包jar 最后名稱自定義的教程,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-10-10
  • Java面向?qū)ο笾饔糜蛟敿?xì)解讀

    Java面向?qū)ο笾饔糜蛟敿?xì)解讀

    這篇文章主要介紹了Java面向?qū)ο笾饔糜蛟敿?xì)解讀,在java編程中,主要的變量就是屬性和局部變量,局部變量一般是指在成員方法中定義的變量,局部變量也就是除了屬性之外的其他變量,作用域?yàn)槎x他的代碼塊中,需要的朋友可以參考下
    2024-01-01
  • 解決Java包裝類比較時(shí)遇到的問題

    解決Java包裝類比較時(shí)遇到的問題

    所謂包裝類的作用就是將原始數(shù)據(jù)類型轉(zhuǎn)換成引用數(shù)據(jù)類型,下面這篇文章主要給大家介紹了關(guān)于在Java包裝類比較時(shí)遇到的問題的解決方法,文中給出了詳細(xì)的示例代碼,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-09-09
  • java、php、C#、asp實(shí)現(xiàn)短信群發(fā)功能的方法

    java、php、C#、asp實(shí)現(xiàn)短信群發(fā)功能的方法

    這篇文章主要介紹了java、php、C#、asp實(shí)現(xiàn)短信群發(fā)功能的方法,以實(shí)例形式較為詳細(xì)的分析了java及php、C#、asp、VB.NET等調(diào)用短信發(fā)送接口進(jìn)行短信發(fā)送的功能,需要的朋友可以參考下
    2015-02-02
  • Spring常用注解及自定義Filter的實(shí)現(xiàn)

    Spring常用注解及自定義Filter的實(shí)現(xiàn)

    這篇文章主要介紹了Spring常用注解及自定義Filter的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08

最新評論

阳原县| 会昌县| 阜城县| 民丰县| 江川县| 临城县| 阿拉善左旗| 丽江市| 陇川县| 礼泉县| 小金县| 临沧市| 都江堰市| 卓资县| 睢宁县| 马鞍山市| 浦江县| 丹阳市| 江源县| 庄河市| 丰镇市| 阿拉尔市| 鄂尔多斯市| 胶州市| 莱州市| 根河市| 临泽县| 佛冈县| 涞源县| 若尔盖县| 安乡县| 益阳市| 明水县| 丹寨县| 望奎县| 清水河县| 四子王旗| 清流县| 汉源县| 镇江市| 巨野县|