1. 程式人生 > >SpringBoot防止重複請求,重複表單提交超級簡單的註解實現之四(終極版)

SpringBoot防止重複請求,重複表單提交超級簡單的註解實現之四(終極版)

前言:上篇文章有的童鞋說不行啊,怎麼不能防止重複提交呢!

首先需要說明的是之前的防止重複提交是指:一次請求完成之前防止重複提交,當然擴充套件下就可以做到會話間防止重複提交,還可以擴充套件為某個時間段或者永久防止重複提交(這個我就不實現了),下面我來擴充套件一下相同會話防止重複提交其實很簡單

在上一篇的基礎上DuplicateAspect不移除標記為SESSION的token就可以了!

1.DuplicateSubmitToken.java新增屬性type,預設為一次請求完成之前防止重複提交

/**
 * @description 防止表單重複提交註解
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface DuplicateSubmitToken {
    /**一次請求完成之前防止重複提交*/
    public static final int REQUEST=1;
    /**一次會話中防止重複提交*/
    public static final int SESSION=2;

    /**儲存重複提交標記 預設為需要儲存*/
    boolean save() default true;

    /**防止重複提交型別,預設:一次請求完成之前防止重複提交*/
    int type() default REQUEST;
}

2.DuplicateSubmitAspect.java方法doAfterReturing判斷如果REQUEST才移除防止重複提交,因為SESSION標記在會話結束 時或失效時會自動移除標記

/**
 * @description 防止表單重複提交攔截器
 */
@Aspect
@Component
@Slf4j
public class DuplicateSubmitAspect {
    public static final String  DUPLICATE_TOKEN_KEY="duplicate_token_key";

    @Pointcut("execution(public * cn.test.controller..*(..))")

    public void webLog() {
    }

    @Before("webLog() && @annotation(token)")
    public void before(final JoinPoint joinPoint, DuplicateSubmitToken token){
        if (token!=null){
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();

            boolean isSaveSession=token.save();
            if (isSaveSession){
                String key = getDuplicateTokenKey(joinPoint);
                Object t = request.getSession().getAttribute(key);
                if (null==t){
                    String uuid= UUID.randomUUID().toString();
                    request.getSession().setAttribute(key.toString(),uuid);
                    log.info("token-key="+key);
                    log.info("token-value="+uuid.toString());
                }else {
                    throw new DuplicateSubmitException(TextConstants.REQUEST_REPEAT);
                }
            }

        }
    }

    /**
     * 獲取重複提交key
     * @param joinPoint
     * @return
     */
    public String getDuplicateTokenKey(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        StringBuilder key=new StringBuilder(DUPLICATE_TOKEN_KEY);
        key.append(",").append(methodName);
        return key.toString();
    }

    @AfterReturning("webLog() && @annotation(token)")
    public void doAfterReturning(JoinPoint joinPoint,DuplicateSubmitToken token) {
        // 處理完請求,返回內容
        log.info("出方法:");
        if (token!=null){
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            boolean isSaveSession=token.save();
            if (isSaveSession){
                String key = getDuplicateTokenKey(joinPoint);
                Object t = request.getSession().getAttribute(key);
                if (null!=t&&token.type()==DuplicateSubmitToken.REQUEST){
                    request.getSession(false).removeAttribute(key);
                }
            }
        }
    }

    /**
     * 異常
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(pointcut = "webLog()&& @annotation(token)", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable e, DuplicateSubmitToken token) {
        if (null!=token
                && e instanceof DuplicateSubmitException==false){
            //處理處理重複提交本身之外的異常
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            boolean isSaveSession=token.save();
            //獲得方法名稱
            if (isSaveSession){
                String key=getDuplicateTokenKey(joinPoint);
                Object t = request.getSession().getAttribute(key);
                if (null!=t){
                    //方法執行完畢移除請求重複標記
                    request.getSession(false).removeAttribute(key);
                    log.info("異常情況--移除標記!");
                }
            }
        }
    }
}

3.使用標記為會話防止重複提交

/**
 * @description
 */
@RestController
public class TestController {

    @DuplicateSubmitToken(type = DuplicateSubmitToken.SESSION)
    @RequestMapping(value = "/test/d", method = RequestMethod.GET)
    public Map<String, Object> test (HttpServletRequest request){

        Random r=new Random();
        int i = r.nextInt(3);
        if (i==2){
            throw new CustomException("有異常");
        }

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Map<String, Object> map = new HashMap<>();
        request.getSession().setAttribute("request Url", request.getRequestURL());
        map.put("request Url", request.getRequestURL());
        return map;
    }

}

4.做了這些可能還是會遇到資料重複插入問題

我們可以根據給建立唯一索引給某個欄位,防止資料不重複插入,或者給方法加鎖(同步)

作者:天空藍藍的     
版權所有,歡迎保留原文連結進行轉載:)