java后端如何實現(xiàn)防止接口重復提交
java后端防止接口重復提交
利用redis實現(xiàn)防止前端重復點擊按鈕重復提交。
解釋:
此方法是利用AOP+Redis實現(xiàn)防止接口重復請求
在需要防止重復提交的方法上,添加CheckRepeatCommit 注解。
此注解額,有兩個屬性,channel屬性是當前訪問的系統(tǒng),redis中key的前綴。
可不填。
expireTime 屬性,是添加了此注解的方法多久之內(nèi)不允許同一用戶重復請求,默認3秒。
防止重復提交的原理就是:
在每次請求加了CheckRepeatCommit注解的接口時,都會利用AOP在redis中保存一個從1開始的自增數(shù)字,并設置此KEY的過期時間,當后續(xù)同一個用戶再次請求時,判斷此自增數(shù)字是否已存在并>=1,如果已存在并>=1,拋出異常。
話不多說,直接上代碼
自定義注解
import java.lang.annotation.*;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CheckRepeatCommit {
String channel() default "APP";
int expireTime() default 3;
}
定義切面
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
@Component
@Aspect
@Slf4j
public class CheckRepeatCommitAspect {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Pointcut("@annotation(com.upbim.twin.park.common.annotation.CheckRepeatCommit)")
private void checkRepeatCommit() {
}
@Around("checkRepeatCommit()")
public Object checkRepeatCommit(ProceedingJoinPoint joinPoint) throws Throwable {
String methodName = joinPoint.getSignature().getName();
Object target = joinPoint.getTarget();
//得到攔截的方法
Method method = getMethodByClassAndName(target.getClass(), methodName);
log.info("驗證是否重復提交:" + "調(diào)用方法:" + method);
//請求的方法名
String className = joinPoint.getTarget().getClass().getName();
String channel = "";
String bizKey = method.getName();
int expireTime = 0;
// 獲取當前請求方法的注解,根據(jù)注解配置獲取參數(shù)
CheckRepeatCommit checkRepeatCommit = method.getAnnotation(CheckRepeatCommit.class);
String userNo = SysUserContextHolder.getSysUser().getUserNo();
String key;
if (checkRepeatCommit != null) {
//注解上的描述
channel = checkRepeatCommit.channel();
expireTime = checkRepeatCommit.expireTime();
key = getRepeatCommitLock(channel, className, bizKey, userNo, expireTime);
if (StringUtils.isBlank(key)) {
throw new SystemException(ResultEnum.RE_COMMIT);
}
}
return joinPoint.proceed();
}
/**
* 根據(jù)類和方法名得到方法
*/
public Method getMethodByClassAndName(Class c, String methodName) {
Method[] methods = c.getDeclaredMethods();
for (Method method : methods) {
if (method.getName().equals(methodName)) {
return method;
}
}
return null;
}
public String getRepeatCommitLock(String channel, String module, String bizKey, String userNo, int expireTime) {
if (StringUtils.isEmpty(module) || StringUtils.isEmpty(bizKey)) {
throw new ParamException(ResultEnum.PARAMETER_CHECK_ERROR, "getRepeatCommitLock{} 參數(shù)不能為空!");
}
String redisKey = channel + StrUtil.COLON + module + StrUtil.COLON + bizKey + StrUtil.COLON + userNo;
long count = redisTemplate.opsForValue().increment(redisKey, 1);
if (count == 1) {
if (expireTime == 0) {
expireTime = 60;
}
redisTemplate.expire(redisKey, expireTime, TimeUnit.SECONDS);
return redisKey;
} else {
return null;
}
}
}
總結
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
SpringBoot整合spring-data-jpa的方法
這篇文章主要介紹了SpringBoot整合spring-data-jpa的方法,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-06-06
詳解spring boot jpa整合QueryDSL來簡化復雜操作
這篇文章主要介紹了詳解spring boot jpa整合QueryDSL來簡化復雜操作,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-04-04
從內(nèi)存方面解釋Java中String與StringBuilder的性能差異
我們通常會發(fā)現(xiàn)使用StringBuffer或StringBuilder創(chuàng)建出來的字符串在拼接時回避String要來得快,尤其是StringBuilder,本文就從內(nèi)存方面解釋Java中String與StringBuilder的性能差異,需要的朋友可以參考下2016-05-05
java調(diào)用chatgpt接口來實現(xiàn)專屬于自己的人工智能助手
這篇文章主要介紹了用java來調(diào)用chatget的接口,實現(xiàn)自己的聊天機器人,對人工智能感興趣的小伙伴可以參考閱讀2023-03-03

