亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

AOP?Redis自定義注解實現(xiàn)細粒度接口IP訪問限制

 更新時間:2022年10月13日 15:24:43   作者:阿桿  
這篇文章主要為大家介紹了AOP?Redis自定義注解實現(xiàn)細粒度接口IP訪問限制,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

系列說明

GitHub地址:github.com/stick-i/scb…

目前項目還有很大改進和完善的空間,歡迎各位有意愿的同學參與項目貢獻(尤其前端),一起學習一起進步??。

項目的技術(shù)棧主要是:

后端 Java + SpringBoot + SpringCloud + Nacos + Getaway + Fegin + MybatisPlus + MySQL + Redis + ES + RabbitMQ + Minio + 七牛云OSS + Jenkins + Docker

前端 Vue + ElementUI + Axios(說實話前端我不太清楚??)

一般向外暴露的接口,都需要加上一個訪問限制,以防止有人惡意刷流量或者爆破,訪問限制的做法有很多種,從控制粒度上來看可以分為:全局訪問限制和接口訪問限制,本文講的是接口訪問的限制。

本章講解的主要內(nèi)容在項目中的位置:

scblogs / common / common-web / src / main / java / cn / sticki / common / web / anno /

我的寫法是基于 AOP + 自定義注解 + Redis,并且封裝在一個單獨的模塊 common-web 下,需要使用的模塊只需引入該包,并且給需要限制的方法添加注解即可,很方便,且松耦合??。

唯一的缺點是該方法只支持在方法上添加注解,不支持給類添加,如果想給一個類的所有方法添加上限制,則必須給該類的所有方法都加上該注解才行??。 如果有同學想把這個缺點完善一下,歡迎到文章頂部的git鏈接中訪問并加入我們的項目??。

實現(xiàn)步驟

一、引入依賴

實現(xiàn)這個功能我們主要需要 Redis 和 AOP的依賴,redis我們用spring的,然后aop使用org.aspectj下的aspectjweaver,主要就是下面這兩個

        <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
        </dependency>

PS:我的項目文件中引入的是我自己的 common-redis 模塊,里面包含了 spring redis的依賴。

二、寫注解

新建一個包,命名為anno,然后在包下新建注解,命名為RequestLimit,再新建一個類,命名為RequestLimitAspect,如下圖:

然后我們先寫注解的內(nèi)容:

package cn.sticki.common.web.anno;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import java.lang.annotation.*;
/**
 * Request 請求限制攔截
 *
 * @author 阿桿
 * @version 1.0
 * @date 2022/7/31 20:19
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface RequestLimit {
	/**
	 * 允許訪問的次數(shù),默認值120
	 */
	int count() default 120;
	/**
	 * 間隔的時間段,單位秒,默認值60
	 */
	int time() default 60;
	/**
	 * 訪問達到限制后需要等待的世界,單位秒,默認值120
	 */
	int waits() default 120;
}

說明:

  • 這里我們設(shè)置@Target(ElementType.METHOD),意思是這個注解只能使用在方法上。
  • 設(shè)置@Order(Ordered.HIGHEST_PRECEDENCE),是為了讓這個注解的的優(yōu)先級升高,也就是先判斷訪問限制,再做其他的事情。
  • 然后注解內(nèi)的參數(shù),是用于不同接口下設(shè)置不同的限制的,使用者可以根據(jù)接口的需求,進行設(shè)置。

三、寫邏輯(注解環(huán)繞)

我們現(xiàn)在基于RequestLimit注解寫環(huán)繞運行的邏輯,也就是開始寫 RequestLimitAspect 的內(nèi)容了,下面都是在這個類中進行操作的。

1. 添加注解

給剛剛新建的 RequestLimitAspect類上使用 @Aspect ,因為等會我們還要把這個類自動注入到Spring當中,所以還得給它加上 @Component 注解。

2. 注入 RedisTemplate

由于我們是要把訪問次數(shù)記錄在redis中的(分布式嘛),所以我們肯定得有 redis 的工具類。

那么問題來了,我們這是個工具模塊,本身并不會被啟動,也沒有啟動類,更沒有什么配置文件,那這種情況下,我們該如何獲得redis呢?

答案是:找引入我們的的模塊要 RedisTemplate。

因為這些Bean都是被spring管控的,包括RedisTemplate,也包括我們現(xiàn)在寫的RequestLimitAspect ,它們將來都是在spring容器內(nèi)的,所以我們直接在代碼里找spring進行注入就可以了。將來引入我們的模塊中如果有RedisTemplate可用,那我們自然就可以拿到。

所以這步很簡單,直接注入即可,但是不要忘了定義一個key前綴,等會用來拼接到redis的key上。

	@Resource
	private RedisTemplate<String, Integer> redisTemplate;
	private static final String IPLIMIT_KEY = "ipLimit:";

3. 定義方法

在類中定義一個before方法,并在方法上使用@Around() 注解,Around內(nèi)填入之前新建的 RequestLimit 的全路徑名,做到這一步,代碼就會像我這樣:

package cn.sticki.common.web.anno;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
 * @author 阿桿
 * @version 1.0
 * @date 2022/7/31 20:24
 */
@Aspect
@Component
@Slf4j
public class RequestLimitAspect {
	@Resource
	private RedisTemplate<String, Integer> redisTemplate;
	private static final String IPLIMIT_KEY = "ipLimit:";
	/**
	 * 攔截有 {@link RequestLimit}注解的方法
	 */
	@Around("@annotation(cn.sticki.common.web.anno.RequestLimit)")
	public Object before(ProceedingJoinPoint pjp) throws Throwable {
		return pjp.proceed();
	}
}

4. 實現(xiàn)方法

步驟:

  • 獲取注解參數(shù)
  • 獲取當前請求的ip
  • 生成key
  • 獲取redis中該key的訪問次數(shù)
  • 判斷次數(shù)是否超過范圍
    • 若超出范圍,則拒絕訪問,返回提示,并將TTL重置為注解上的等待時間
    • 若沒有超過范圍,則允許訪問,并將訪問次數(shù)+1
    • 若查詢不到該key,則往redis中進行添加,將值設(shè)置為1,將TTL設(shè)置為注解上的值

完整實現(xiàn)代碼如下(內(nèi)容干凈無毒,可以放心CV,僅需將返回值進行修改):

package cn.sticki.common.web.anno;
import cn.sticki.common.result.RestResult;
import cn.sticki.common.web.utils.RequestUtils;
import cn.sticki.common.web.utils.ResponseUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
/**
 * @author 阿桿
 * @version 1.0
 * @date 2022/7/31 20:24
 */
@Aspect
@Component
@Slf4j
public class RequestLimitAspect {
	@Resource
	private RedisTemplate<String, Integer> redisTemplate;
	private static final String IPLIMIT_KEY = "ipLimit:";
	/**
	 * 攔截有 {@link RequestLimit}注解的方法
	 */
	@Around("@annotation(cn.sticki.common.web.anno.RequestLimit)")
	public Object before(ProceedingJoinPoint pjp) throws Throwable {
		MethodSignature signature = (MethodSignature) pjp.getSignature();
		// 1. 獲取被攔截的方法和方法名
		Method method = signature.getMethod();
		String methodName = signature.getDeclaringTypeName() + "." + signature.getName();
		log.debug("攔截方法{}", methodName);
		// 1.2 獲取注解參數(shù)
		RequestLimit limit = method.getAnnotation(RequestLimit.class);
		// 2. 獲取當前線程的請求
		ServletRequestAttributes attribute = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		if (attribute == null) {
			log.warn(this.getClass().getName() + "只能用于web controller方法");
			return pjp.proceed();
		}
		HttpServletRequest request = attribute.getRequest();
		// 2.2 獲取當前請求的ip
		String ip = RequestUtils.getIpAddress(request);
		// 3. 生成key
		String key = IPLIMIT_KEY + methodName + ":" + ip;
		// 4. 獲取Redis中的數(shù)據(jù)
		Integer count = redisTemplate.opsForValue().get(key);
		int nowCount = count == null ? 0 : count;
		if (nowCount >= limit.count()) {
			// 5. 超出限制,拒絕訪問
			assert attribute.getResponse() != null;
			log.info("訪問頻繁被拒絕訪問,ip:{},method:{}", ip, signature.getName());
			ResponseUtils.objectToJson(attribute.getResponse(), RestResult.fail("訪問頻繁"));
			if (nowCount == limit.count()) {
				// 5.2 重置Redis時間為設(shè)定的等待值
				log.debug("重置redis值為{},等待{}", nowCount + 1, limit.waits());
				redisTemplate.opsForValue().set(key, nowCount + 1, limit.waits(), TimeUnit.SECONDS);
			}
			return null;
		}
		if (count == null) {
			// 重置計數(shù)器
			log.debug("重置計數(shù)器");
			redisTemplate.opsForValue().set(key, 1, limit.time(), TimeUnit.SECONDS);
		} else {
			// 計數(shù)器 +1,不重置TTL
			redisTemplate.opsForValue().increment(key);
		}
		log.debug("方法放行");
		return pjp.proceed();
	}
}

5. 開啟spring自動裝配

spring會自動注入spring.factories文件中的類,所以我們只需要編寫spring.factories即可。

首先在resources下新建META-INF文件夾,然后在該文件夾下新建文件,命名為spring.factories

文件內(nèi)容如下:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  cn.sticki.common.web.anno.RequestLimitAspect

這里的全限定名需要改為自己的類路徑名。

四、測試

  • 把剛剛寫的那個模塊用maven進行本地打包

  • 然后在其他服務(wù)中引入該模塊為依賴,對需要進行訪問限制的方法使用。

運行項目

訪問該接口進行測試

剛開始正常

多次訪問之后被拒絕

查看redis數(shù)據(jù),發(fā)現(xiàn)符合我設(shè)定的條件

總結(jié)

本文講解了如何在微服務(wù)中優(yōu)雅的實現(xiàn)一個公用的接口訪問限制工具,更多關(guān)于AOP Redis 接口IP訪問限制的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Redis的鍵String全面詳解

    Redis的鍵String全面詳解

    這篇文章主要為大家介紹了Redis的鍵String全面詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-06-06
  • 基于session?Redis實現(xiàn)登錄

    基于session?Redis實現(xiàn)登錄

    這篇文章主要介紹了基于session?Redis實現(xiàn)登錄的相關(guān)資料,需要的朋友可以參考下
    2023-10-10
  • 手把手教你用Redis 實現(xiàn)點贊功能并且與數(shù)據(jù)庫同步

    手把手教你用Redis 實現(xiàn)點贊功能并且與數(shù)據(jù)庫同步

    本文主要介紹了Redis 實現(xiàn)點贊功能并且與數(shù)據(jù)庫同步,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-05-05
  • Redis中的數(shù)據(jù)結(jié)構(gòu)跳表詳解

    Redis中的數(shù)據(jù)結(jié)構(gòu)跳表詳解

    跳表是一種基于并聯(lián)的鏈表結(jié)構(gòu),用于在有序元素序列中快速查找元素的數(shù)據(jù)結(jié)構(gòu),本文給大家介紹Redis中的數(shù)據(jù)結(jié)構(gòu)跳表,感興趣的朋友跟隨小編一起看看吧
    2024-06-06
  • 詳解Redis中的List類型

    詳解Redis中的List類型

    這篇文章主要介紹了Redis中的List類型,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-11-11
  • 淺談Redis中的RDB快照

    淺談Redis中的RDB快照

    雖說Redis是內(nèi)存數(shù)據(jù)庫,但是它為數(shù)據(jù)的持久化提供了兩個技術(shù),分別是AOF日志和RDB快照。這兩種技術(shù)都會用各用一個日志文件來記錄信息,但是記錄的內(nèi)容是不同的。AOF 文件的內(nèi)容是操作命令; RDB 文件的內(nèi)容是二進制數(shù)據(jù)。本文將討論RDB快照的原理和使用
    2021-06-06
  • k8s部署redis哨兵的實現(xiàn)

    k8s部署redis哨兵的實現(xiàn)

    本文主要介紹了k8s部署redis哨兵的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-07-07
  • Redis分布式鎖與Redlock算法實現(xiàn)

    Redis分布式鎖與Redlock算法實現(xiàn)

    在Redis中,可以使用多種方式實現(xiàn)分布式鎖,如使用SETNX命令或RedLock算法,本文就來介紹一下Redis分布式鎖與Redlock算法實現(xiàn),感興趣的可以了解一下
    2023-12-12
  • 華為歐拉openEuler編譯安裝Redis的實現(xiàn)步驟

    華為歐拉openEuler編譯安裝Redis的實現(xiàn)步驟

    本文主要介紹了華為歐拉openEuler編譯安裝Redis的實現(xiàn)步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-01-01
  • redis中RDB(Redis Data Base)的機制

    redis中RDB(Redis Data Base)的機制

    本文主要介紹了redis中RDB(Redis Data Base)的機制,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-04-04

最新評論