java实现幂等性校验

我们在做web应用的时候通常会遇到前端提交按钮重复点击的场景,在某些新增操作上就需要做幂等性限制来保证数据的可靠性。下面来用java aop实现幂等性校验。

一:首先我们需要一个自定义注解

复制代码
package com.yuku.yuku_erp.annotation;

import java.lang.annotation.*;

/**
 * @author 名一
 * @ClassName IdempotentAnnotation
 * @description: 用来标记需要校验幂等性的接口
 * @datetime 2024年 02月 03日 14:48
 * @version: 1.0
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IdempotentAnnotation {
    String idempotentType();
}

二:创建一个幂等校验的切面类

复制代码
package com.yuku.yuku_erp.aop;

import com.yuku.yuku_erp.annotation.IdempotentAnnotation;
import com.yuku.yuku_erp.constant.RedisKeyConstant;
import com.yuku.yuku_erp.exception.MyException;
import com.yuku.yuku_erp.utils.RedisShardedPoolUtil;
import com.yuku.yuku_erp.utils.TokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @author 名一
 * @ClassName CheckIdempotentAop
 * @description: 幂等性校验
 * @datetime 2024年 02月 03日 14:59
 * @version: 1.0
 */
@Slf4j
@Aspect
@Component
public class CheckIdempotentAop {

    @Pointcut("execution(* com.yuku.yuku_erp.controller..*.*(..))")
    public void checkCut(){
    }

    @Before("checkCut()")
    public void checkIdempotent(JoinPoint joinPoint){
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        if (method.isAnnotationPresent(IdempotentAnnotation.class)){
            IdempotentAnnotation annotation = method.getAnnotation(IdempotentAnnotation.class);
            String idempotentType = annotation.idempotentType();
            String idempotentToken = TokenUtil.getRequest().getHeader("idempotentToken");
            String idemToken = idempotentType + idempotentToken;
            log.info("checkIdempotent idempotentType:{}, idempotentToken:{}", idempotentType, idempotentToken);

            Boolean flag = RedisShardedPoolUtil.sismember(RedisKeyConstant.IDEMPOTENT_TOKEN_LIST, idemToken);
            if (!flag){
                log.error("checkIdempotent error idempotentType:{}, idempotentToken:{}, flag:{}", idempotentType, idempotentToken, flag);
                throw new MyException("该接口已提交过,请不要重复提交");
            }
            RedisShardedPoolUtil.delSetByValue(RedisKeyConstant.IDEMPOTENT_TOKEN_LIST, idemToken);
            log.info("checkIdempotent idempotentType:{}, idempotentToken:{}, flag:{}", idempotentType, idempotentToken, flag);
        }
    }
}

三:在需要切面的接口上使用幂等校验注解

复制代码
@IdempotentAnnotation(idempotentType = "checkIdempotentToken")
    @GetMapping("/checkIdempotentToken")
    @ApiOperation(value = "校验幂等性示例")
    public CommonResult<String> checkIdempotentToken(){
        return CommonResult.success();
    }

到此幂等校验就完成了