|
|
@ -1,15 +1,16 @@ |
|
|
|
package com.epmet.commons.tools.aop; |
|
|
|
|
|
|
|
import com.epmet.commons.tools.constant.NumConstant; |
|
|
|
import com.epmet.commons.tools.distributedlock.DistributedLock; |
|
|
|
import com.epmet.commons.tools.exception.EpmetErrorCode; |
|
|
|
import com.epmet.commons.tools.exception.RenException; |
|
|
|
import com.google.common.cache.Cache; |
|
|
|
import com.google.common.cache.CacheBuilder; |
|
|
|
import com.epmet.commons.tools.redis.RedisKeys; |
|
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
|
import org.aspectj.lang.JoinPoint; |
|
|
|
import org.aspectj.lang.ProceedingJoinPoint; |
|
|
|
import org.aspectj.lang.annotation.Around; |
|
|
|
import org.aspectj.lang.annotation.Aspect; |
|
|
|
import org.aspectj.lang.annotation.Before; |
|
|
|
import org.aspectj.lang.annotation.Pointcut; |
|
|
|
import org.redisson.api.RLock; |
|
|
|
import org.springframework.beans.factory.annotation.Autowired; |
|
|
|
import org.springframework.context.annotation.Configuration; |
|
|
|
import org.springframework.web.context.request.RequestContextHolder; |
|
|
|
import org.springframework.web.context.request.ServletRequestAttributes; |
|
|
@ -18,6 +19,7 @@ import javax.servlet.http.HttpServletRequest; |
|
|
|
import java.util.concurrent.TimeUnit; |
|
|
|
|
|
|
|
/** |
|
|
|
* desc:利用用户token及请求url 防止用户重复提交,需要在没有唯一索引的保存或修改操作 方法入口上加上@NoRepeatSubMit注解 |
|
|
|
* @author zhaoqifeng |
|
|
|
* @dscription |
|
|
|
* @date 2020/11/24 9:59 |
|
|
@ -26,51 +28,49 @@ import java.util.concurrent.TimeUnit; |
|
|
|
@Configuration |
|
|
|
@Slf4j |
|
|
|
public class NoRepeatSubmitAop { |
|
|
|
private static final String AUTHORIZATION_TOKEN_HEADER_KEY = "Authorization"; |
|
|
|
|
|
|
|
@Autowired |
|
|
|
private DistributedLock distributedLock; |
|
|
|
|
|
|
|
/** |
|
|
|
* 重复提交判断时间为2s |
|
|
|
*/ |
|
|
|
private static final Cache<String, Integer> CACHES = CacheBuilder.newBuilder() |
|
|
|
// 最大缓存 100 个
|
|
|
|
.maximumSize(100) |
|
|
|
// 设置写缓存后 5 秒钟过期
|
|
|
|
.expireAfterWrite(5, TimeUnit.SECONDS) |
|
|
|
.build(); |
|
|
|
@Around("@annotation(noRepeatSubmit)") |
|
|
|
public Object around(ProceedingJoinPoint pjp,NoRepeatSubmit noRepeatSubmit) { |
|
|
|
|
|
|
|
@Pointcut("@annotation(com.epmet.commons.tools.aop.NoRepeatSubmit)") |
|
|
|
public void doAspect() { |
|
|
|
try { |
|
|
|
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); |
|
|
|
assert attributes != null; |
|
|
|
HttpServletRequest request = attributes.getRequest(); |
|
|
|
String internalToken = request.getHeader(AUTHORIZATION_TOKEN_HEADER_KEY); |
|
|
|
String key = getKey(request.getRequestURI(), internalToken); |
|
|
|
// 如果缓存中有这个url视为重复提交
|
|
|
|
Object result = null; |
|
|
|
try { |
|
|
|
long leaseTime = noRepeatSubmit.leaseTime(); |
|
|
|
//如果获取不到锁等待0秒直接返回 持锁时间为leaseTime
|
|
|
|
RLock lock = distributedLock.getLock(RedisKeys.getNoRepeatSubmitKey(key), leaseTime, NumConstant.ZERO_L, TimeUnit.MILLISECONDS); |
|
|
|
try { |
|
|
|
//因为getLock如果获取失败抛异常 所以不做锁状态的判断
|
|
|
|
result = pjp.proceed(); |
|
|
|
} finally { |
|
|
|
distributedLock.unLock(lock); |
|
|
|
} |
|
|
|
} catch (Exception e) { |
|
|
|
log.warn("noRepeatSubmit exception",e); |
|
|
|
//"未获取到锁,重复提交了
|
|
|
|
throw new RenException(EpmetErrorCode.REPEAT_SUBMIT.getCode()); |
|
|
|
} |
|
|
|
|
|
|
|
return result; |
|
|
|
} catch (RenException e) { |
|
|
|
throw e; |
|
|
|
} catch (Throwable e) { |
|
|
|
log.error("验证重复提交时出现未知异常!"); |
|
|
|
throw new RenException(EpmetErrorCode.SERVER_ERROR.getCode()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@Before("doAspect()") |
|
|
|
public void around(JoinPoint pjp) { |
|
|
|
try { |
|
|
|
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); |
|
|
|
assert attributes != null; |
|
|
|
HttpServletRequest request = attributes.getRequest(); |
|
|
|
String key = getKey(request.getRequestURI(), pjp.getArgs()); |
|
|
|
// 如果缓存中有这个url视为重复提交
|
|
|
|
if (CACHES.getIfPresent(key) == null) { |
|
|
|
CACHES.put(key, NumConstant.ZERO); |
|
|
|
} else { |
|
|
|
log.error("重复提交"); |
|
|
|
throw new RenException(EpmetErrorCode.REPEATED_SUBMIT_ERROR.getCode()); |
|
|
|
} |
|
|
|
} catch (RenException e) { |
|
|
|
throw e; |
|
|
|
} catch (Throwable e) { |
|
|
|
log.error("验证重复提交时出现未知异常!"); |
|
|
|
throw new RenException(EpmetErrorCode.SERVER_ERROR.getCode()); |
|
|
|
} |
|
|
|
private String getKey(String keyExpress,String token) { |
|
|
|
return keyExpress+token; |
|
|
|
} |
|
|
|
|
|
|
|
private String getKey(String keyExpress, Object[] args) { |
|
|
|
for (int i = 0; i < args.length; i++) { |
|
|
|
keyExpress = keyExpress.replace("arg[" + i + "]", args[i].toString()); |
|
|
|
} |
|
|
|
return keyExpress; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|