微服务中使用AOP实现防止接口二次提交

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/wangchengming1/article/details/102651023

无论在微服务还是单体程序中,防止接口二次提交都是必须要解决方法,现在已经有成熟的解决方案,比如采用点击一次后让按钮置灰,等请求结束后再可以点击。当然后端也要解决这个问题。

采用AOP的方式防止接口二次提交
  • 思路
    • 以唯一标识为key,任意值为value,存入redis(本地缓存也可以),并设置一个合理的过期时间。
    • 将注解用在新增、修改等接口上。
    • 每次调用时根据key判断,缓存是否存在,存在则抛出异常或提示,不存在则执行业务逻辑。
  • 定义一个注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmitCheck {
    int keepSeconds() default 5;
}
  • 实现这个注解
@Aspect
@Component
@Slf4j
public class RepeatSubmitAspect {

    @Resource
    private RedisService redisService;
 
    @Pointcut("@annotation(com.xxx.xxx.annotation.RepeatSubmitCheck)")
    public void requestPointcut() {
    }
 
    @Around("requestPointcut()")
    public void aroundCheck(JoinPoint joinPoint, RepeatSubmitCheck repeatSubmitCheck) {
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        String paramsJsonStr = JSON.toJSONString(args);
 
        String srcStr = className + "_" + methodName + "_" + paramsJsonStr;
        String signStr = Md5Utils.md5(srcStr);
        log.info("防止重复提交的请求 Redis Key:" + signStr);
 
        //判断缓存是否存在不存在则添加缓存
        if(!redisService.exists(signStr)){
            redisService.setex(signStr, repeatSubmitCheck.keepSeconds(), "1");
        } else {//重复请求
            log.info("重复提交的请求数据:" + srcStr);
            throw new RuntimeException("重复请求");
        }
    }
}
  • RedisService部分代码

@AllArgsConstructor
public class RedisService {

	private RedisTemplate<String, Object> redisTemplate;

	public boolean exists(String key) {
        return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            return connection.exists(key.getBytes());
        });
    }
    
	public boolean setex(final String key, long expire, final String value) {
        return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
            return connection.setEx(serializer.serialize(key), expire, serializer.serialize(value));
        });
    }
}
  • 使用方法
@PostMapping("/repeatSubmit")
@RepeatSubmitCheck
public String testRepeatSubmit() {
    return "test RepeatSubmitCheck";
}

猜你喜欢

转载自blog.csdn.net/wangchengming1/article/details/102651023