使用註釋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<>();
}

測試:
在這裏插入圖片描述
在這裏插入圖片描述
撒花!完成!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章