前端也可以处理这个问题,到时绕过前端的情况,就需要后端处理了。
后端处理逻辑:自定义注解+AOP+Redis自动过期key。
1.首先自定义一个注解@NoRepeatSubmit
// 作用到方法上
@Target(ElementType.METHOD)
// 运行时有效
@Retention(RetentionPolicy.RUNTIME)
public @interface NoRepeatSubmit {
/**
* 默认时间3秒
*/
int time() default 3 * 1000;
}
2.在需要拦截的controller上加上注解,作为pointcut切入点。
@RestController
public class HomeController {
@NoRepeatSubmit
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String login(@RequestParam String userName) {
System.out.println(userName);
return userName;
}
@NoRepeatSubmit
@RequestMapping(value = "/login2", method = RequestMethod.GET)
public String login2(@RequestParam String userName2) {
System.out.println(userName2);
return userName2;
}
}
3.在切面类中判断是否重复提交。
@Slf4j
@Aspect
@Component
public class NoRepeatSubmitAspect {
@Autowired
private RedisTemplate redisTemplate;
@Pointcut("@annotation(noRepeatSubmit)")
public void pointcut(NoRepeatSubmit noRepeatSubmit) {
}
@Before("pointcut(noRepeatSubmit)")
public void before(final JoinPoint joinPoint, NoRepeatSubmit noRepeatSubmit) throws Exception {
log.info("joinPoint:"+joinPoint);
//获取目标类的名称
String target = joinPoint.getTarget().getClass().getName();
//获取目标类的方法
String methodName = joinPoint.getSignature().getName();
log.info("target:"+target);
log.info("methodName:"+methodName);
HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
log.info("request:"+request);
String url = request.getRequestURL().toString();
log.info("url:"+url);
String token = request.getHeader("token");
log.info("token:"+token);
//自行定义符合业务需要的key
String redisKey = target+"_"+methodName+"_"+token;
if ( redisTemplate.hasKey(redisKey)) {
throw new Exception( "重复提交");
}else {
//此处设置的1秒过期
redisTemplate.opsForValue().set(redisKey, "不要重复提交!!!",1000, TimeUnit.MILLISECONDS);
log.info("value:::::"+redisTemplate.opsForValue().get(redisKey));
}
}
}
发起请求
当快速点击两次,就会报错啦