1. 程式人生 > 其它 >後端--防重複提交策略方法

後端--防重複提交策略方法

技術標籤:javaredisjavaspring

後端–防重複提交策略方法

原因:

前臺操作的抖動,快速操作,網路通訊或者後端響應慢,都會增加後端重複處理的概率。
情形

  1. 由於使用者誤操作,多次點選表單提交按鈕。
  2. 由於網速等原因造成頁面卡頓,使用者重複重新整理提交頁面。
  3. 黑客或惡意使用者使用postman等工具重複惡意提交表單(攻擊網站)。
  4. 這些情況都會導致表單重複提交,造成資料重複,增加伺服器負載,嚴重甚至會造成伺服器宕機。因此有效防止表單重複提交有一定的必要性。

解決方案:

一:給資料庫增加唯一鍵約束
navicat在這裡插入圖片描述
這種方法需要在程式碼中加入捕捉插入資料異常:

 try {
	            // insert
} catch (DuplicateKeyException e) { logger.error("user already exist"); }

二:使用AOP自定義切入實現
實現原理:

1.自定義防止重複提交標記(@AvoidRepeatableCommit)。   
2.對需要防止重複提交的Congtroller裡的mapping方法加上該註解。   
3.新增Aspect切入點,為@AvoidRepeatableCommit加入切入點。 
4.每次提交表單時,Aspect都會儲存當前key到reids(須設定過期時間)。   
5.
重複提交時Aspect會判斷當前redis是否有該key,若有則攔截。
/**
		 * 避免重複提交
		 */
		@Target(ElementType.METHOD)
		@Retention(RetentionPolicy.RUNTIME)
		public @interface AvoidRepeatableCommit {
		
		    /**
		     * 指定時間內不可重複提交,單位秒
		     * @return
		     */
		    long timeout()  default 3 ;
		
		}
/**
		 * 重複提交aop 
		 */
		@Aspect
@Component public class AvoidRepeatableCommitAspect { @Autowired private RedisTemplate redisTemplate; /** * @param point */ @Around("@annotation(com.xwolf.boot.annotation.AvoidRepeatableCommit)") public Object around(ProceedingJoinPoint point) throws Throwable { HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest(); String ip = IPUtil.getIP(request); //獲取註解 MethodSignature signature = (MethodSignature) point.getSignature(); Method method = signature.getMethod(); //目標類、方法 String className = method.getDeclaringClass().getName(); String name = method.getName(); String ipKey = String.format("%s#%s",className,name); int hashCode = Math.abs(ipKey.hashCode()); String key = String.format("%s_%d",ip,hashCode); log.info("ipKey={},hashCode={},key={}",ipKey,hashCode,key); AvoidRepeatableCommit avoidRepeatableCommit = method.getAnnotation(AvoidRepeatableCommit.class); long timeout = avoidRepeatableCommit.timeout(); if (timeout < 0){ //過期時間5分鐘 timeout = 60*5; } String value = (String) redisTemplate.opsForValue().get(key); if (StringUtils.isNotBlank(value)){ return "請勿重複提交"; } redisTemplate.opsForValue().set(key, UUIDUtil.uuid(),timeout,TimeUnit.MILLISECONDS); //執行方法 Object object = point.proceed(); return object; } }