防止重复提交
防止重复提交
前端
节流
解释:持续触发高频事件,函数每n秒执行一次。
业务场景
输入框的模糊查询
目的
节约流量、内存的损耗,旨在提升性能,在高频率频发的事件中才会用到。
防抖
持续触发高频事件,函数只会执行一次。
后端
目的:防止绕过前端校验持续访问接口。
实现
切面类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63package com.xiaoruiit.common.aop;
import com.alibaba.druid.util.StringUtils;
import com.xiaoruiit.common.utils.IPUtil;
import com.xiaoruiit.common.utils.IdWorker;
import com.xiaoruiit.common.utils.Result;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
/**
* @author hxr
*/
@Component
@Aspect // 定义切面类
public class AvoidDuplicateSubmitAspect {
@Autowired
private RedisTemplate redisTemplate;
@Around("@annotation(com.xiaoruiit.common.aop.AvoidDuplicateSubmit)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
// 获取request对象
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
String ip = IPUtil.getIP(request);
// 获取方法签名
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
String className = method.getDeclaringClass().getName();
String methodName = method.getName();
String ipKey = String.format("%s#%s", className, methodName);
int hashCode = Math.abs(ipKey.hashCode());
// 拼接redisKey,如:127.0.0.1_1234984393
String redisKey = String.format("%s_%d", ip, hashCode);
String value = (String) redisTemplate.opsForValue().get(redisKey);
if (!StringUtils.isEmpty(value)) {
return Result.validateFailed("请勿重复提交");
}
// 获取注解
AvoidDuplicateSubmit avoidDuplicateSubmit = method.getAnnotation(AvoidDuplicateSubmit.class);
long timeout = avoidDuplicateSubmit.timeout();
if (timeout < 0) {
timeout = 5000;
}
// 第一次提交,插入redis
redisTemplate.opsForValue().set(redisKey, IdWorker.nextId(), timeout, TimeUnit.MILLISECONDS);
// 继续执行方法
return joinPoint.proceed();
}
}注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19package com.xiaoruiit.common.aop;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author hxr
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AvoidDuplicateSubmit {
/**
* 指定时间内不可重复提交,单位毫秒
* @return
*/
long timeout() default 5000;
}
使用
1
2
3方法上加注解 @AvoidDuplicateSubmit
@AvoidDuplicateSubmit 默认时间5000毫秒
@AvoidDuplicateSubmit(timeout = 100000) 单位毫秒自己设置控制时间
获取IP真实地址
代理服务器结合java代码
1.nginx配置
java代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23private static final String[] HEADERS_TO_TRY = {
"X-Forwarded-For",
"Proxy-Client-IP",
"WL-Proxy-Client-IP",
"HTTP_X_FORWARDED_FOR",
"HTTP_X_FORWARDED",
"HTTP_X_CLUSTER_CLIENT_IP",
"HTTP_CLIENT_IP",
"HTTP_FORWARDED_FOR",
"HTTP_FORWARDED",
"HTTP_VIA",
"REMOTE_ADDR" };
private String getClientIpAddress(HttpServletRequest request) {
for (String header : HEADERS_TO_TRY) {
String ip = request.getHeader(header);
if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
return ip;
}
}
return request.getRemoteAddr();
}