切面拦截请求
/**
* @ClassName RequestDataAspect
* @Description 数据重复提交校验
* @Author yhh
* @Date 2021/3/9 9:25
**/
@Logger
@Aspect
@Component
public class ResubmitDataAspect {
private final static Object PRESENT = new Object();
@Around("@annotation(com.fs.his.nurse.annotation.Resubmit)")
public Object handleResubmit(ProceedingJoinPoint joinPoint) throws Throwable {
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
//获取注解信息
Resubmit annotation = method.getAnnotation(Resubmit.class);
int delaySeconds = annotation.delaySeconds();
RequestInfo info = new RequestInfo();
//请求ip
info.setIp(request.getRemoteAddr());
//请求方式
info.setMethod(request.getMethod());
//请求URL
info.setUrl(request.getRequestURL().toString());
//请求方法
info.setRequestMethod(joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
String key = "";
if("POST".equals(request.getMethod())){
if (joinPoint.getArgs().length > 0) {
for (Object o : joinPoint.getArgs()) {
//只取自定义的参数, 自带的HttpServletRequest, HttpServletResponse不管
if (o instanceof HttpServletRequest || o instanceof HttpServletResponse) {
continue;
}
info.setRequestParams(o);
}
}
}else if("GET".equals(request.getMethod())){
info.setRequestParams(request.getParameterMap());
}
String toJSONString = JSON.toJSONString(info);
//解析参数
JSONObject requestDTO = JSONObject.parseObject(toJSONString);
if (requestDTO != null) {
StringBuffer sb = new StringBuffer();
requestDTO.forEach((k, v) -> {
sb.append(v);
});
//生成加密参数 使用了content_MD5的加密方式
key = ResubmitLock.handleKey(sb.toString());
}
//执行锁
boolean lock = false;
try {
//设置解锁key
lock = ResubmitLock.getInstance().lock(key, PRESENT);
if (lock) {
//放行
return joinPoint.proceed();
} else {
return new AjaxResponseObject(false,"资源已提交,请稍等");
}
} finally {
//设置解锁key和解锁时间
ResubmitLock.getInstance().unLock(lock, key, delaySeconds);
}
}
}
核心代码:加密解密请求数据
/**
* @author yhh
* 重复提交锁
*/
@Logger
public final class ResubmitLock {
private static final ConcurrentHashMap<String, Object> LOCK_CACHE = new ConcurrentHashMap<>(200);
private static final ScheduledThreadPoolExecutor EXECUTOR = new ScheduledThreadPoolExecutor(5, new ThreadPoolExecutor.DiscardPolicy());
// private static final Cache<String, Object> CACHES = CacheBuilder.newBuilder()
// 最大缓存 100 个
// .maximumSize(1000)
// 设置写缓存后 5 秒钟过期
// .expireAfterWrite(5, TimeUnit.SECONDS)
// .build();
private ResubmitLock() {
}
/**
* 静态内部类 单例模式
*
* @return
*/
private static class SingletonInstance {
private static final ResubmitLock INSTANCE = new ResubmitLock();
}
public static ResubmitLock getInstance() {
return SingletonInstance.INSTANCE;
}
public static String handleKey(String param) {
return DigestUtils.md5Hex(param == null ? "" : param);
}
/**
* 加锁 putIfAbsent 是原子操作保证线程安全
*
* @param key 对应的key
* @param value
* @return
*/
public boolean lock(final String key, Object value) {
return Objects.isNull(LOCK_CACHE.putIfAbsent(key, value));
}
/**
* 延时释放锁 用以控制短时间内的重复提交
*
* @param lock 是否需要解锁
* @param key 对应的key
* @param delaySeconds 延时时间
*/
public void unLock(final boolean lock, final String key, final int delaySeconds) {
if (lock) {
EXECUTOR.schedule(() -> {
LOCK_CACHE.remove(key);
}, delaySeconds, TimeUnit.SECONDS);
}
}
}
封装请求实体
/**
* @author yhh
* @Description:
* @date 2020/10/23 14:15
*/
public class RequestInfo {
private String ip;
private String url;
private String method;
private String requestMethod ;
private Object requestParams;
申明注解作用范围
/**
* @author yhh
* @Description:
* @date 2021/3/9 9:16
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Resubmit {
/**
* 延长多久允许再次提交
*/
int delaySeconds() default 20 ;
}