springboot防止表單重復(fù)提交方式
第一種方法:單個防止
在Spring Boot應(yīng)用中使用Redis來防止表單的重復(fù)提交,可以通過以下幾個步驟來實(shí)現(xiàn):
步驟 1:添加依賴
確保你的項(xiàng)目中添加了Spring Boot Starter Data Redis和Spring Boot Starter Web依賴。
在pom.xml文件中添加以下依賴:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
步驟 2:配置Redis
在application.properties或application.yml中配置Redis服務(wù)器的信息:
spring.redis.host=localhost spring.redis.port=6379
步驟 3:創(chuàng)建一個工具類處理Redis操作
創(chuàng)建一個工具類來封裝Redis的一些常用操作,比如設(shè)置鍵值對、獲取鍵值對等。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
@Component
public class RedisUtil {
@Autowired
private StringRedisTemplate stringRedisTemplate;
public void set(String key, String value) {
stringRedisTemplate.opsForValue().set(key, value);
}
public String get(String key) {
return stringRedisTemplate.opsForValue().get(key);
}
public void delete(String key) {
stringRedisTemplate.delete(key);
}
}
步驟 4:實(shí)現(xiàn)防重邏輯
在Controller中使用上面的工具類來實(shí)現(xiàn)防重邏輯。
通常的做法是在請求開始時檢查Redis中是否存在該請求的唯一標(biāo)識(如用戶ID + 時間戳),如果不存在則允許請求繼續(xù),并將此標(biāo)識存入Redis;如果存在,則直接返回錯誤信息。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@Autowired
private RedisUtil redisUtil;
@PostMapping("/submit")
public String submit(@RequestParam("userId") String userId) {
// 使用用戶ID和時間戳作為key,防止并發(fā)下的重復(fù)提交
String key = "form_submit_" + userId + "_" + System.currentTimeMillis();
if (redisUtil.get(key) != null) {
return "請求重復(fù),請勿重復(fù)提交!";
} else {
// 執(zhí)行業(yè)務(wù)邏輯
// ...
// 將key存入Redis并設(shè)置過期時間
redisUtil.set(key, "1", 5, TimeUnit.MINUTES);
return "提交成功!";
}
}
}
請注意:
在實(shí)際應(yīng)用中,你可能需要根據(jù)具體需求調(diào)整上述代碼,比如更改存儲在Redis中的鍵的生成方式、設(shè)置更合理的過期時間等。
此外,為了提高性能和避免并發(fā)問題,可以考慮使用Redis的原子操作,例如SETNX命令。
第二種方法:aop全局方法
使用AOP(面向切面編程)來統(tǒng)一處理防重復(fù)提交是一種很好的實(shí)踐,它能減少重復(fù)代碼并保持業(yè)務(wù)邏輯的清晰性。下面是如何使用Spring AOP來實(shí)現(xiàn)這一功能的示例。
首先,你需要在項(xiàng)目中添加Spring AOP的支持,通常情況下Spring Boot項(xiàng)目已經(jīng)默認(rèn)包含AOP支持。
步驟 1:創(chuàng)建切面類
創(chuàng)建一個切面類,用于定義防重提交的邏輯。
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Aspect
@Component
public class AntiDuplicateSubmissionAspect {
private static final String KEY_PREFIX = "anti_duplicate_";
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Around("@annotation(antiDuplicateSubmission)")
public Object antiDuplicateSubmit(ProceedingJoinPoint joinPoint, AntiDuplicateSubmission antiDuplicateSubmission) throws Throwable {
// 從注解中獲取參數(shù)名
String paramName = antiDuplicateSubmission.value();
// 獲取方法參數(shù)
Object[] args = joinPoint.getArgs();
// 假設(shè)參數(shù)位置已知,或者通過參數(shù)名獲取參數(shù)值
String userId = (String) args[0];
// 生成唯一標(biāo)識符
String key = KEY_PREFIX + userId + "_" + System.currentTimeMillis();
// 檢查是否已經(jīng)存在
if (stringRedisTemplate.hasKey(key)) {
throw new RuntimeException("請求重復(fù),請勿重復(fù)提交!");
}
// 設(shè)置過期時間,例如5分鐘
stringRedisTemplate.opsForValue().set(key, "1", 5, TimeUnit.MINUTES);
try {
return joinPoint.proceed();
} finally {
// 清理key,這一步在大多數(shù)情況下可選,因?yàn)樵O(shè)置了過期時間
stringRedisTemplate.delete(key);
}
}
}
步驟 2:創(chuàng)建自定義注解
創(chuàng)建一個自定義注解,用于標(biāo)記需要防重提交的控制器方法。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AntiDuplicateSubmission {
String value();
}
步驟 3:應(yīng)用注解
在需要防重提交的Controller方法上應(yīng)用這個注解。
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@PostMapping("/submit")
@AntiDuplicateSubmission(value = "userId")
public String submit(@RequestParam("userId") String userId) {
// 執(zhí)行業(yè)務(wù)邏輯
// ...
return "提交成功!";
}
}
這樣,每次有請求到達(dá)帶有@AntiDuplicateSubmission注解的方法時,AOP切面會先執(zhí)行防重檢查,如果發(fā)現(xiàn)重復(fù)提交,則阻止業(yè)務(wù)邏輯執(zhí)行并拋出異常。
注意:
這里的AntiDuplicateSubmission注解的value屬性應(yīng)該與請求參數(shù)名匹配,以便正確地獲取到用戶ID或其他用于生成唯一標(biāo)識符的參數(shù)。
同時,使用System.currentTimeMillis()生成的時間戳作為一部分唯一標(biāo)識符可能在高并發(fā)場景下存在并發(fā)問題,你可能需要一個更安全的唯一生成策略,例如UUID或者結(jié)合用戶ID、請求時間以及隨機(jī)數(shù)等生成一個更復(fù)雜的唯一標(biāo)識符。
總結(jié)
以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring Boot 集成 ElasticSearch應(yīng)用小結(jié)
這篇文章主要介紹了Spring Boot 集成 ElasticSearch應(yīng)用小結(jié),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-11-11
IDEA新建springboot項(xiàng)目時未生成pom.xml文件的解決操作
這篇文章主要給大家介紹了關(guān)于IDEA新建springboot項(xiàng)目時未生成pom.xml文件的解決操作方法,文中通過實(shí)例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2023-02-02
Java Web stmp發(fā)送帶附件郵件(附SSL版)
這篇文章主要為大家詳細(xì)介紹了Java Web stmp發(fā)送帶附件郵件,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-05-05
MyBatis-Plus updateById方法更新不了空字符串/null的問題及解決
MyBatis-Plus的updateById()方法在更新字段為null時會失敗,因?yàn)槟J(rèn)策略不更新null值,文章提供三種解決方案:全局配置忽略判斷、使用PO對象的el屬性指定jdbcType、通過注解設(shè)置字段驗(yàn)證策略為IGNORED2026-03-03
java一個接口多個實(shí)現(xiàn)類的調(diào)用方式
這篇文章主要給大家介紹了關(guān)于java一個接口多個實(shí)現(xiàn)類的調(diào)用方式的相關(guān)資料,經(jīng)測試確認(rèn),當(dāng)一個接口有多個實(shí)現(xiàn)時,調(diào)用時只會執(zhí)行一個,有時候需要多個實(shí)現(xiàn)調(diào)用,需要的朋友可以參考下2023-09-09
RabbitMQ冪等性與優(yōu)先級及惰性詳細(xì)全面講解
關(guān)于MQ消費(fèi)者的冪等性問題,在于MQ的重試機(jī)制,因?yàn)榫W(wǎng)絡(luò)原因或客戶端延遲消費(fèi)導(dǎo)致重復(fù)消費(fèi)。使用MQ重試機(jī)制需要注意的事項(xiàng)以及如何解決消費(fèi)者冪等性與優(yōu)先級及惰性問題以下將逐一講解2022-11-11

