前言
某些时候,总有些艺高人胆大的家伙喜欢乱来,所以基于Redis写了一个限制访问次数的功能.
需要用到AOP实现(本文使用),当然,也可以使用拦截器实现,看个人喜好吧.
实现
导入依赖
<!-- AOP依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>
自定义注解
创建一个自定义注解来标记需要限制访问的接口,当然你需要限制全部当我没说..
/**
* @author GAS
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AccessLimit {
//每秒
int seconds();
//最大访问次数
int maxcount();
//是否判断登录
boolean needLogin() default true;
}
自定义AOP
/**
* @author LZC
* @Date: 2020/11/1 0:12
* @Description: 接口防刷拦截器
*/
@Aspect
@Component
public class LimitVisitAspect {
@Autowired
private RedisUtil redisUtil;
@Pointcut("@annotation(accessLimit)")
public void excudeService(AccessLimit accessLimit) {
}
@Around("excudeService(accessLimit)")
public Object doAround(ProceedingJoinPoint pjp, AccessLimit accessLimit) throws Throwable {
// 获得request对象
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
HttpServletRequest request = sra.getRequest();
String uri = request.getRequestURI();
String ip = request.getRemoteAddr();
String key = "limit:" + ip + "-" + uri;
String s = redisUtil.get(key);
//我这里因为项目需求 简单粗暴 单日只能访问一次
if(s == null){
// result的值就是被拦截方法的返回值
Object result = pjp.proceed();
redisUtil.set(key,"1");
return result;
}
return AjaxResult.success("访问过多了");
}
/**
* 判断IP地址是否为内外IP
* 没用到
* @param addr
* @return
*/
public static boolean internalIp(byte[] addr) {
//使用方式
//if (ip.equals("0:0:0:0:0:0:0:1") || ip.equals("127.0.0.1")) {
// return true;
//}
//byte[] addr = IPAddressUtil.textToNumericFormatV4(ip);
final byte b0 = addr[0];
final byte b1 = addr[1];
//10.x.x.x/8
final byte SECTION_1 = 0x0A;
//172.16.x.x/12
final byte SECTION_2 = (byte) 0xAC;
final byte SECTION_3 = (byte) 0x10;
final byte SECTION_4 = (byte) 0x1F;
//192.168.x.x/16
final byte SECTION_5 = (byte) 0xC0;
final byte SECTION_6 = (byte) 0xA8;
switch (b0) {
case SECTION_1:
return true;
case SECTION_2:
if (b1 >= SECTION_3 && b1 <= SECTION_4) {
return true;
}
case SECTION_5:
switch (b1) {
case SECTION_6:
return true;
}
default:
return false;
}
}
}
- 获取到
IP
和访问URL
将其作为一个key,存放在Redis中,并记录访问次数,如果超过返回错误或者其他消息
定时删除限制
/**
* 每天清除redis的限制访问列表
* 当然 也可以给redis设置过期时间
* 但是我就是要这样写
*
* @author GAS
* @date 2021年08月30日 16:42
*/
@Component
@EnableScheduling
@EnableAsync
public class CleanLimitTask {
@Autowired
private RedisUtil redisUtil;
@Async
@Scheduled(cron = "0 0 23 * * ?")//每天23点执行
public void cleanLimit(){
Set<String> keys = redisUtil.keys("limit:*");
redisUtil.delete(keys);
}
}
最后在你需要限制的地方加上注解就行了
@Controller
public class testController{
@RequestMapping("/hello")
@AccessLimit(seconds = 5, maxcount = 1)
public String test(HttpServletRequest request){
return "hello";
}
}
参考
文章1: springboot限制接口访问次数 - CSDN
文章2: SpringBoot实现限制ip访问次数_It_BeeCoder的博客-CSDN博客_springboot 限制访问频率
文章3: springboot接口限制访问次数_战战的坚果的博客-CSDN博客
文章4: SpringBoot在一定时间内限制接口请求次数_qq_41084438的博客-CSDN博客_springboot限制接口访问次数