java 使用redis校验 防止网络延迟表单重复提交
一、引入redis依赖
<!-- 引入redis配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
二、配置redis连接信息
spring:
redis: #以下redis部分自行修改
host: localhost
port: 6379
password: 密码
database: 1 #选择一号库
三、创建一个注解类
使用注解配置你要切哪个方法需要校验(一般是增删改方法,查询一般不需要校验)
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 网络抖动校验注解
*/
@Target(ElementType.METHOD)//只能在方法上使用
@Retention(RetentionPolicy.RUNTIME)//运行时生效
public @interface AntiExplosion {
}
四、创建一个切面类
使用环绕切面,切添加注解的方法
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* 校验网络抖动配置
*/
@Configuration
@Aspect //定义这是切面类
public class AntiExplosionAspect {
@Resource
private StringRedisTemplate stringRedisTemplate;
/**
* 切方法上添加此注解的接口信息
*/
@Around("@annotation(com.test.zsq.annotaion.AntiExplosion)")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//获取redis操作
ValueOperations<String, String> stringRedis = stringRedisTemplate.opsForValue();
//获取当前方法所有的参数信息
List<Object> args = Arrays.asList(proceedingJoinPoint.getArgs());
Object proceed = null;
//判断是否存在参数
if (args.size() != 0) {
//获取方法名
String name = proceedingJoinPoint.getSignature().getName();
//将参数转成json字符串
String jsonString = JSON.toJSONString(args);
//进行MD5加密(为了减少key存储长度,你自己随便找一个方式)
String build = MD5Util.build(jsonString);
String key = "test-server:prevent:" + name + ":" + build;
//获取redis中的数据
String antiShake = stringRedis.get(key);
if (name.equals(antiShake)) {
throw new CommonException("正在执行中,请勿重复操作");
}
// //判断当前执行的方法是否执行过,让线程进行等待
// while (name.equals(antiShake)) {
// Thread.sleep(1000);
// antiShake = stringRedis.get("test-server:prevent:" + build);
// }
//执行后续的代码时添加一个缓存
stringRedis.set(key, name, 5, TimeUnit.MINUTES);
try {
//向下执行接口
proceed = proceedingJoinPoint.proceed();
} finally {
//清除redis中的缓存
stringRedisTemplate.delete(key);
}
}
return proceed;
}
}
使用方式,只需要在要校验的方法上添加注解
@RestController
@RequestMapping("/test")
public class TestController{
@Resource
private TestCertService testService;
//将需要校验的方法上添加校验注解即可
@AntiExplosion
@RequestMapping("add")
public Long add(User user) {
return testService.add(user);
}
}