天天看點

使用注釋AOP實作接口通路次數限制

前言

在日常開發工作中,我們常有接口會暴露出來,雖然我們增加了各種檢驗和攔截可以攔截大多數惡意通路,但是你不能保證對接方的猿子不會造出一個死循環來通路你的接口,尤其是我們的程式作為一個平台使用的時候,别人的一個誤操作可能會造成伺服器當機,到時候成千上萬的客戶都會受到影響,是以在這種對接過程中一定要對對方的接口通路次數進行限制!這種方式可以了解為微服務中的服務降級!

安排栗子

建立一個注釋類:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LimitTime {
    // 通路次數,預設為10次
    int time() default 10;
    // 過期時間,時間戳間隔
    long timeout() default 1;
}      

定義一個存放調用資訊的DTO:

@Data
public class LimitDTO {
    //最近一次重新整理時間戳
    private Long refreshTime;
    //剩餘通路次數
    private Integer time;
}      

建立一個切面類:

注意在存儲通路狀态對象的時候一定要使用ConcurrentHashMap,此為線程安全的map,支援并發通路!

@Component
@Order
@Aspect
public class LimitTimeAspect {
    private ConcurrentHashMap<String, LimitDTO> limitMap = new ConcurrentHashMap<>();

    @Pointcut("@annotation(limitTime)")
    public void limit(LimitTime limitTime) {
    }

    @Around("limit(limitTime)")
    public Object aroundLog(ProceedingJoinPoint joinpoint, LimitTime limitTime) throws Throwable {
        //擷取傳入的最大通路次數
        int time= limitKey.time();
        //擷取計算時間
        long timeout = limitKey.timeout();
        //擷取通路方法
        Object target = joinpoint.getTarget().getClass().getName();
        String key= target.toString();
        //如果第一次通路該方法
        if (limitMap.get(key) == null) {
            //建立一次對象存放通路資訊
            LimitDTO limitDTO=new LimitDTO();
            limitDTO.setTime(time- 1);
            limitDTO.setRefreshTime(new Date().getTime());
            limitMap.put(key, limitDTO);
        }else {
            //如果不是第一次通路,擷取上次通路的資訊
            LimitDTO limitDTO=limitMap.get(key);
            //如果和上次重新整理時間比已經過期
            if (new Date().getTime() - limitDTO.getRefreshTime() > timeout) {
                //将對象中的重新整理時間和通路次數重新整理
                limitDTO.setRefreshTime(new Date().getTime());
                limitDTO.setTime(time);
                limitMap.put(key, limitDTO);
            }
            //擷取目前通路對象中的剩餘通路次數
            int t = (int) limitMap.get(key).getTime;
            //如果通路次數大于0
            if (t > 0) {
                //允許通路,并将通路次數-1
                limitDTO.setTime(--t);
            } else {
                //如果已經沒有通路次數,傳回錯誤資訊
                ResultBO<Object> resultBO = new ResultBO<>();
                resultBO.setCode(1);
                resultBO.setMsg("已達最大通路次數");
                return resultBO;
            }
        }
        //列印資訊
        System.err.println("剩餘次數:" + limitMap.get(key).getTime + " 方法名稱:" + key);
        return joinpoint.proceed();
    }

      

使用該注釋:

@RequestMapping(value = "/sendCmd", method = RequestMethod.POST)
@ResponseBody
@LimitKey(time= 10, timeout = 10000)//10秒内可以通路10次
public int sendCmd(@RequestBody List<CmdDO> cmds) {
    //do something
    return new ResultBO<>();
}

      

測試:

使用注釋AOP實作接口通路次數限制
使用注釋AOP實作接口通路次數限制

撒花!完成!

繼續閱讀