SpringBoot接口防重復提交的三種解決方案
前言
在Web開發(fā)中,防止用戶重復提交表單是一個常見的需求。用戶可能會因為網(wǎng)絡(luò)延遲、誤操作等原因多次點擊提交按鈕,導致后臺接收到多個相同的請求。這不僅會浪費服務(wù)器資源,還可能導致數(shù)據(jù)不一致等問題。本文將介紹幾種在Spring Boot中實現(xiàn)接口防重復提交的方法。
使用Token機制
Token機制是一種常見的防重復提交方法。具體步驟如下:
生成Token:用戶每次請求表單頁面時,服務(wù)器生成一個唯一的Token,并將其存儲在Session中。
傳遞Token:將Token嵌入到表單中,隨表單一起提交。
驗證Token:服務(wù)器接收到請求后,首先驗證Token是否有效,如果有效則繼續(xù)處理請求,并從Session中移除該Token;如果無效,則返回錯誤信息。
實現(xiàn)步驟
1.生成Token
在Controller中生成Token并存儲在Session中:
/** * form * @param session * @author senfel * @date 2024/11/12 11:29 * @return org.springframework.web.servlet.ModelAndView */ @GetMapping("/form") public ModelAndView showForm(HttpSession session) { ModelAndView form = new ModelAndView("form"); String token = UUID.randomUUID().toString(); session.setAttribute("token", token); form.addObject("token", token); return form; }
2.傳遞Token
在表單中添加隱藏字段來傳遞Token:
<form action="/base/submit" method="post"> <input type="hidden" name="token" th:value="${token}"> <!-- 其他表單字段 --> <button type="submit">Submit</button> </form>
3.驗證Token
在Controller中驗證Token:
/** * handleForm * @param token * @param session * @author senfel * @date 2024/11/12 11:34 * @return java.lang.String */ @PostMapping("/submit") public String handleForm(@RequestParam String token, HttpSession session) { String sessionToken = (String) session.getAttribute("token"); if (sessionToken == null || !sessionToken.equals(token)) { throw new RuntimeException("Duplicate submit detected"); } // 移除Token session.removeAttribute("token"); // 處理表單數(shù)據(jù) return "success"; }
使用Redis
Redis是一個高性能的鍵值存儲系統(tǒng),可以用來存儲和驗證Token。具體步驟如下:
生成Token:用戶每次請求表單頁面時,服務(wù)器生成一個唯一的Token,并將其存儲在Redis中。
傳遞Token:將Token嵌入到表單中,隨表單一起提交。
驗證Token:服務(wù)器接收到請求后,首先驗證Token是否存在于Redis中,如果存在則繼續(xù)處理請求,并從Redis中刪除該Token;如果不存在,則返回錯誤信息。
實現(xiàn)步驟
1.引入Redis依賴
在 pom.xml 中添加Redis依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
2.生成Token
在Controller中生成Token并存儲在Redis中:
@Autowired private StringRedisTemplate redisTemplate; /** * formByRedis * @author senfel * @date 2024/11/12 11:50 * @return org.springframework.web.servlet.ModelAndView */ @GetMapping("/formByRedis") public ModelAndView showFormByRedis() { ModelAndView form = new ModelAndView("form"); String token = UUID.randomUUID().toString(); // 設(shè)置過期時間 redisTemplate.opsForValue().set(token, token, 5, TimeUnit.MINUTES); form.addObject("token", token); return form; }
3.傳遞Token
在表單中添加隱藏字段來傳遞Token:
<form action="/base/submitByRedis" method="post"> <input type="hidden" name="token" th:value="${token}"> <!-- 其他表單字段 --> <button type="submit">Submit</button> </form>
4.驗證Token
在Controller中驗證Token:
/** * submitByRedis * @param token * @author senfel * @date 2024/11/12 11:50 * @return java.lang.String */ @PostMapping("/submitByRedis") public String handleFormByRedis(@RequestParam String token) { String redisToken = redisTemplate.opsForValue().get(token); if (redisToken == null) { throw new RuntimeException("Duplicate submit detected"); } // 刪除Token redisTemplate.delete(token); // 處理表單數(shù)據(jù) return "success"; }
使用Spring AOP
Spring AOP(Aspect-Oriented Programming)可以用來實現(xiàn)切面編程,從而在多個方法中復用防重復提交的邏輯。
實現(xiàn)步驟
1.定義注解
創(chuàng)建一個自定義注解 @PreventDuplicateSubmit:
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * PreventDuplicateSubmit * @author senfel * @date 2024/11/12 11:56 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface PreventDuplicateSubmit { /**重復請求時間*/ int expireSeconds() default 10; }
2.創(chuàng)建切面
創(chuàng)建一個切面類 DuplicateSubmitAspect:
import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; import java.lang.reflect.Method; import java.util.concurrent.TimeUnit; /** * DuplicateSubmitAspect * @author senfel * @version 1.0 * @date 2024/11/12 11:57 */ @Slf4j @Aspect @Component public class DuplicateSubmitAspect { protected static final Logger logger = LoggerFactory.getLogger(DuplicateSubmitAspect.class); @Autowired private StringRedisTemplate redisTemplate; /** * around * @param joinPoint * @author senfel * @date 2024/11/12 15:45 * @return java.lang.Object */ @Around("@annotation(com.example.ccedemo.aop.PreventDuplicateSubmit)") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { StringBuilder key = new StringBuilder(); //獲取class String simpleName = joinPoint.getTarget().getClass().getSimpleName(); key.append(simpleName); // 獲取請求方法 MethodSignature signature=(MethodSignature)joinPoint.getSignature(); Method method = signature.getMethod(); String methodName = method.getName(); key.append(":").append(methodName); //獲取請求參數(shù) Object[] args=joinPoint.getArgs(); for (Object arg : args) { key.append(":").append(arg.toString()); } //TODO 獲取客戶端IP // 獲取注解信息 PreventDuplicateSubmit annotation = method.getAnnotation(PreventDuplicateSubmit.class); // 判斷是否已經(jīng)請求過 if(redisTemplate.hasKey(key.toString())){ throw new RuntimeException("請勿重復提交"); } //標記請求已經(jīng)處理過 redisTemplate.opsForValue().set(key.toString(),"1",annotation.expireSeconds(), TimeUnit.SECONDS); return joinPoint.proceed(); } }
3.使用注解
在Controller方法上使用 @PreventDuplicateSubmit 注解:
/** * handleFormByAnnotation * @param param * @author senfel * @date 2024/11/12 11:59 * @return java.lang.String */ @PostMapping("/submitByAnnotation") @PreventDuplicateSubmit public String handleFormByAnnotation(@RequestParam String param) { // 處理表單數(shù)據(jù) return "success"; }
總結(jié)
本文介紹了三種在Spring Boot中實現(xiàn)接口防重復提交的方法:使用Token機制、使用Redis和使用Spring AOP。每種方法都有其適用場景和優(yōu)缺點,可以根據(jù)實際需求選擇合適的方法。通過這些方法,可以有效防止用戶重復提交表單,提高系統(tǒng)的穩(wěn)定性和用戶體驗。
以上就是SpringBoot接口防重復提交的三種解決方案的詳細內(nèi)容,更多關(guān)于SpringBoot接口防重復提交的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Jedis出現(xiàn)connection timeout問題解決方法(JedisPool連接池使用實例)
這篇文章主要介紹了Jedis出現(xiàn)connection timeout問題解決方法,使用Jedis的JedisPool連接池解決了這個問題,需要的朋友可以參考下2014-05-05解決mybatis 執(zhí)行mapper的方法時報空指針問題
這篇文章主要介紹了解決mybatis 執(zhí)行mapper的方法時報空指針問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07Java 基礎(chǔ) byte[]與各種數(shù)據(jù)類型互相轉(zhuǎn)換的簡單示例
這篇文章主要介紹了Java 基礎(chǔ) byte[]與各種數(shù)據(jù)類型互相轉(zhuǎn)換的簡單示例的相關(guān)資料,這里對byte[]類型對long,int,double,float,short,cahr,object,string類型相互轉(zhuǎn)換的實例,需要的朋友可以參考下2017-01-01使用SpringBoot開發(fā)Restful服務(wù)實現(xiàn)增刪改查功能
Spring Boot是由Pivotal團隊提供的全新框架,其設(shè)計目的是用來簡化新Spring應(yīng)用的初始搭建以及開發(fā)過程。這篇文章主要介紹了基于SpringBoot開發(fā)一個Restful服務(wù),實現(xiàn)增刪改查功能,需要的朋友可以參考下2018-01-01java中多個@Scheduled定時器不執(zhí)行的解決方法
在應(yīng)用開發(fā)中經(jīng)常需要一些周期性的操作,比如每5分鐘執(zhí)行某一操作等,這篇文章主要給大家介紹了關(guān)于java中多個@Scheduled定時器不執(zhí)行的解決方法,需要的朋友可以參考下2023-04-04