当前位置:   article > 正文

Springboot实现接口幂等性校验_springboot幂等性校验

springboot幂等性校验

何为幂等性校验

通俗理解,同一个接口用相同参数请求多次,必须保证操作只执行一次。
比如,支付接口因为网络原因或其他因素,导致重复支付,这将导致难以弥补的损失,软件的可靠性也会受到客户质疑。

幂等性的解决方案

  • 唯一索引,使用唯一索引可以避免脏数据的添加,当插入重复数据是数据库会抛出异常
  • 乐观锁,增加version版本号,当数据需要更新时,只能成功一次,其余更新操作会由于version的不同,导致更新失败。
  • token机制,在操作前先掉后端服务请求一个唯一凭证,在请求时带上后端校验,一个token只能执行一次。

无侵入实现方案

实现思路

  • 自定义一个幂等校验的注解,属性包含 间隔时间、异常提示内容
  • 通过切面对有注解的方案进行幂等校验
  • 将方法的参数进行MD5加密(MD5加密同一个内容,加密后的内容是不变的),将密文作为key,间隔时间作为value放入缓存
  • 切面前置通知通过缓存和请求参数MD5加密后的内容进行对比,判断是否重复提交

具体实现代码

自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Idempotent {

  /**
   * 间隔时间(ms),小于此时间视为重复提交
   */
  long interval() default 3000l;

  /**
   * 提示消息
   */
  String message() default "不允许重复提交,请稍候再试";
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

切面

@Aspect
@Component
@Slf4j
public class IdempotentAspect {

  @Autowired
  private CacheManager cacheManager;

  /**
   * 缓存key
   */
  private final String idempotentCacheKey = "idempotent";

  @Pointcut("@annotation(com.xxx.xxx.aspect.idempotent.Idempotent)")
  public void pointCut() {

  }

  @Before("pointCut()")
  public void checkIsRepeatSubmit(JoinPoint joinPoint) {
    log.info("********开始幂等性校验********");
    MethodSignature signature = (MethodSignature) joinPoint.getSignature();
    Idempotent annotation = signature.getMethod().getAnnotation(Idempotent.class);
    //判断是否重复提交
    if (this.isRepeatSubmit(joinPoint.getArgs(), annotation.interval())) {
      throw new CommonException(annotation.message());
    }
  }

  /**
   * 判断是否重复执行
   * @param args 请求参数
   * @param interval 过期时间
   * @return 是否重复执行
   */
  private boolean isRepeatSubmit(Object[] args, Long interval) {
    String key = DigestUtil.md5Hex(Arrays.toString(args));
    Cache.ValueWrapper valueWrapper = cacheManager.getCache(idempotentCacheKey).get(key);
    //缓存为空,则认为第一次提交
    if (Objects.isNull(valueWrapper)) {
      cacheManager.getCache(idempotentCacheKey).put(key, System.currentTimeMillis());
      return false;
    }
    //缓存过期
    if (System.currentTimeMillis() >= (Long) valueWrapper.get() + interval) {
      cacheManager.getCache(idempotentCacheKey).put(key, System.currentTimeMillis());
      return false;
    }
    return true;
  }
}
  • 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

注意

验证的方法有很多,使用缓存时一定要设置缓存的过期时间,否则会导致缓存大量的无意义数据。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/425867
推荐阅读
相关标签
  

闽ICP备14008679号