前言

某些时候,总有些艺高人胆大的家伙喜欢乱来,所以基于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限制接口访问次数