偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

別讓接口被瘋狂點(diǎn)擊!Spring Boot 防重實(shí)戰(zhàn):哈希 + 緩存雙保險(xiǎn)方案實(shí)測(cè)!

開(kāi)發(fā) 前端
本文將帶你實(shí)現(xiàn)一種“哈希 + 緩存”雙重保障的接口防重復(fù)提交方案,?無(wú)需前端配合,不依賴額外 Token,僅通過(guò)請(qǐng)求特征動(dòng)態(tài)生成哈希簽名,即可快速判斷重復(fù)請(qǐng)求。?我們將基于?Spring Boot + AOP + Redis/Caffeine?的架構(gòu)實(shí)現(xiàn)這一機(jī)制,輕量高效,實(shí)戰(zhàn)級(jí)可復(fù)用。

在高并發(fā)的業(yè)務(wù)場(chǎng)景中,接口被重復(fù)點(diǎn)擊或短時(shí)間內(nèi)多次提交請(qǐng)求,是一個(gè)常見(jiàn)但極具破壞性的隱患。 例如,電商系統(tǒng)中用戶點(diǎn)擊“提交訂單”按鈕多次,可能會(huì)生成重復(fù)訂單; 又如支付接口被多次觸發(fā),造成重復(fù)扣費(fèi); 或者表單接口因網(wǎng)絡(luò)抖動(dòng)被重新提交,產(chǎn)生臟數(shù)據(jù)。

這些問(wèn)題雖然看似小概率事件,但在真實(shí)生產(chǎn)環(huán)境中往往導(dǎo)致嚴(yán)重后果。 為了避免此類“重復(fù)提交”的混亂,我們需要在服務(wù)端層面構(gòu)建一個(gè)高可靠的防重機(jī)制

本文將帶你實(shí)現(xiàn)一種“哈希 + 緩存”雙重保障的接口防重復(fù)提交方案, 無(wú)需前端配合,不依賴額外 Token,僅通過(guò)請(qǐng)求特征動(dòng)態(tài)生成哈希簽名,即可快速判斷重復(fù)請(qǐng)求。 我們將基于 Spring Boot + AOP + Redis/Caffeine 的架構(gòu)實(shí)現(xiàn)這一機(jī)制,輕量高效,實(shí)戰(zhàn)級(jí)可復(fù)用。

防重原理與方案選型

什么是防重復(fù)提交

防重復(fù)提交(Prevent Duplicate Request)指的是防止用戶在短時(shí)間內(nèi)對(duì)同一接口重復(fù)觸發(fā)操作,從而造成數(shù)據(jù)重復(fù)創(chuàng)建、狀態(tài)異常或邏輯錯(cuò)誤。

例如:

  • 下單接口:防止同一個(gè)用戶同時(shí)創(chuàng)建兩筆相同訂單;
  • 表單提交:防止頁(yè)面卡頓或多次點(diǎn)擊產(chǎn)生重復(fù)記錄;
  • 支付操作:防止短時(shí)間內(nèi)重復(fù)支付。

常見(jiàn)實(shí)現(xiàn)方式

實(shí)現(xiàn)方式

原理說(shuō)明

優(yōu)缺點(diǎn)

前端防重

按鈕加 loading,或禁用二次點(diǎn)擊

簡(jiǎn)單但不可靠,可被繞過(guò)

Token 標(biāo)識(shí)

每次請(qǐng)求生成唯一 Token,校驗(yàn)后銷毀

安全性高,但依賴前端

請(qǐng)求特征哈希(推薦)

通過(guò)請(qǐng)求路徑、方法、參數(shù)生成唯一哈希值進(jìn)行校驗(yàn)

無(wú)需前端依賴,后端即可防重

本文采用第三種方式,通過(guò) URL + 請(qǐng)求方法 + 請(qǐng)求參數(shù) 構(gòu)造一個(gè)全局唯一哈希值,并將其存儲(chǔ)在緩存中。 當(dāng)檢測(cè)到相同哈希在有效期內(nèi)再次出現(xiàn)時(shí),即判定為重復(fù)請(qǐng)求。

系統(tǒng)架構(gòu)與流程設(shè)計(jì)

目錄結(jié)構(gòu)如下

/src
 └── /main
     ├── /java/com/icoderoad/duplicate
     │    ├── annotation/PreventDuplicate.java
     │    ├── aspect/PreventDuplicateAspect.java
     │    ├── storage/DuplicateStorage.java
     │    ├── storage/impl/RedisStorage.java
     │    ├── storage/impl/CaffeineStorage.java
     │    └── util/RequestParameterUtils.java
     └── /resources
          └── application.yml

防重復(fù)機(jī)制核心流程如下:

  1. 請(qǐng)求進(jìn)入控制層;
  2. AOP 攔截目標(biāo)方法
  3. 提取 URL、請(qǐng)求方法、參數(shù)信息;
  4. 計(jì)算 SHA-256 哈希值作為 Key
  5. 查詢緩存(Redis/Caffeine)是否存在該 Key;
  6. 存在則拒絕請(qǐng)求,不存在則執(zhí)行方法并寫(xiě)入緩存。

核心實(shí)現(xiàn)代碼

自定義注解 @PreventDuplicate
package com.icoderoad.duplicate.annotation;


import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;


/**
 * 防重復(fù)提交注解
 * 可應(yīng)用在 Controller 層接口上
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PreventDuplicate {


    /** 防重復(fù)提交時(shí)間(單位:秒) */
    int expire() default 3;


    /** 時(shí)間單位,默認(rèn)秒 */
    TimeUnit timeUnit() default TimeUnit.SECONDS;


    /** 可選指定參與生成哈希的主要字段 */
    String[] field() default {};


    /** 提示信息 */
    String message() default "請(qǐng)勿重復(fù)提交!";
}
AOP 攔截器 PreventDuplicateAspect
package com.icoderoad.duplicate.aspect;


import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.core.date.DateTime;
import com.icoderoad.duplicate.annotation.PreventDuplicate;
import com.icoderoad.duplicate.storage.DuplicateStorage;
import com.icoderoad.duplicate.storage.DuplicateStorageFactory;
import com.icoderoad.duplicate.util.RequestParameterUtils;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;


@Aspect
@Component
@RequiredArgsConstructor
public class PreventDuplicateAspect {


    private final HttpServletRequest request;
    private final DuplicateStorageFactory storageFactory;


    @Around("@annotation(preventDuplicate)")
    public Object handle(ProceedingJoinPoint joinPoint, PreventDuplicate preventDuplicate) throws Throwable {
        String method = request.getMethod();
        String uri = request.getRequestURI();
        String params = RequestParameterUtils.getAllParamsAsString(joinPoint, preventDuplicate.field());


        // 拼接唯一簽名源
        String signSource = method + ":" + uri + ":" + params;
        long start = System.currentTimeMillis();
        String key = DigestUtil.sha256Hex(signSource);
        long end = System.currentTimeMillis();
        System.out.println("生成哈希耗時(shí):" + (end - start) + "ms");


        DuplicateStorage storage = storageFactory.getStorage();
        if (storage.exists(key)) {
            throw new RuntimeException(preventDuplicate.message());
        }


        storage.put(key, preventDuplicate.expire(), preventDuplicate.timeUnit());
        return joinPoint.proceed();
    }
}
控制層示例
package com.icoderoad.duplicate.controller;


import cn.hutool.core.date.DateTime;
import com.icoderoad.duplicate.annotation.PreventDuplicate;
import com.icoderoad.duplicate.model.ArticleDTO;
import com.icoderoad.duplicate.model.UserInfo;
import org.springframework.web.bind.annotation.*;


@RestController
@RequestMapping("/demo")
public class DemoController {


    @GetMapping("/hello")
    @PreventDuplicate
    public String hello(String name, String age, String address) {
        return "防重復(fù)測(cè)試:" + name + " " + age + " " + address;
    }


    @PostMapping("/saveUserInfo")
    @PreventDuplicate(expire = 5)
    public String saveUserInfo(@RequestBody UserInfo userInfo) {
        System.out.println(userInfo);
        return "請(qǐng)求時(shí)間:" + DateTime.now() + " 保存成功";
    }


    @PostMapping("/saveContent")
    @PreventDuplicate(expire = 10)
    public String saveContent(@RequestBody ArticleDTO articleDTO) {
        System.out.println(articleDTO);
        return "請(qǐng)求時(shí)間:" + DateTime.now() + " 內(nèi)容保存成功";
    }
}

測(cè)試結(jié)果: 當(dāng)短時(shí)間內(nèi)重復(fù)發(fā)送相同參數(shù)請(qǐng)求時(shí),系統(tǒng)將直接返回 "請(qǐng)勿重復(fù)提交!" 異常提示。

性能驗(yàn)證

為了驗(yàn)證哈希計(jì)算的性能,我們生成了一篇 3 萬(wàn)字文章內(nèi)容并進(jìn)行請(qǐng)求測(cè)試。 結(jié)果顯示:

  • 首次生成哈希值耗時(shí)約 9ms(JVM 預(yù)熱階段);
  • 多次請(qǐng)求后平均耗時(shí)降至 0ms
  • 即使請(qǐng)求參數(shù)極大,對(duì)性能幾乎無(wú)影響。

結(jié)論: SHA-256 哈希算法在防重場(chǎng)景中既具唯一性又具高性能,完全可滿足高并發(fā)接口防重復(fù)的需求。

總結(jié)與實(shí)踐建議

通過(guò)本方案,我們實(shí)現(xiàn)了一個(gè)無(wú)侵入、通用性強(qiáng)、性能優(yōu)異的防重復(fù)提交機(jī)制。 核心要點(diǎn)包括:

  1. 使用 AOP 切面攔截 請(qǐng)求,避免侵入業(yè)務(wù)邏輯;
  2. 基于 請(qǐng)求路徑 + 方法 + 參數(shù)哈希 生成唯一標(biāo)識(shí);
  3. 通過(guò) Redis / Caffeine 緩存 實(shí)現(xiàn)分布式與本地防重雙模式;
  4. 支持靈活配置提交間隔與關(guān)鍵字段粒度。

該方案不僅可用于表單、下單、支付等關(guān)鍵接口,也可擴(kuò)展至異步任務(wù)提交、API 冪等控制等更廣泛場(chǎng)景。

未來(lái)還可以進(jìn)一步優(yōu)化:

  • 加入 異步清理機(jī)制
  • 對(duì) Key 結(jié)構(gòu)添加命名空間前綴;
  • 結(jié)合 分布式鎖 提升在集群環(huán)境下的安全性。

一句話總結(jié):

防重不是“錦上添花”的優(yōu)化,而是“防止災(zāi)難”的必要保護(hù)。 用哈希 + 緩存雙保險(xiǎn),為你的接口上好“安全帶”!

責(zé)任編輯:武曉燕 來(lái)源: 路條編程
相關(guān)推薦

2025-02-28 13:00:00

SpringBoot接口接口安全

2021-04-26 08:54:17

Spring BootSecurity防重登錄

2025-07-28 01:00:00

2013-12-17 19:21:11

通達(dá)OA

2012-10-24 17:47:48

2012-05-24 10:23:17

天融信安全管理體系

2014-11-24 11:52:45

華為醫(yī)療

2025-02-21 12:00:00

SpringBoot防重復(fù)提交緩存機(jī)制

2025-03-26 03:25:00

SpringGuavaCaffeine

2021-10-15 11:34:27

奇安信鯤鵬

2025-02-22 08:00:00

AgentSpringBootJava

2025-06-06 08:28:56

2025-06-12 08:21:22

2024-05-28 09:26:46

2025-05-14 04:00:00

2024-06-14 09:30:58

2024-07-26 07:59:25

2018-11-02 15:45:41

Spring BootRedis數(shù)據(jù)庫(kù)

2020-04-23 15:59:04

SpringKafka集群

2024-08-29 09:01:39

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)