Spring boot 对接口限制IP访问次数

1、需要的依赖

        <!-- redis依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!-- AOP依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <!-- Map依赖 用redis就不需要了 -->
        <dependency>
            <groupId>net.jodah</groupId>
            <artifactId>expiringmap</artifactId>
            <version>0.5.8</version>
        </dependency>

2、定义注解

package org.springblade.modules.aspect.annotation;


import java.lang.annotation.*;

/**
 * 接口访问频率
 *
 * @author
 * @date 2022/08/27
 */
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InterfaceAccessRestriction {

    /**
     * 时间毫秒 单位:毫秒(默认:1分钟)
     *
     * @return long
     */
    long time() default 60000;

    /**
     * 允许请求的次数 默认 30
     *
     * @return int
     */
    int value() default 30;
}

3、切面实现

借助redis实现

package org.springblade.modules.aspect;

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.annotation.Pointcut;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.api.ResultCode;
import org.springblade.modules.aspect.annotation.InterfaceAccessRestriction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;


/**
 * 接口访问频率
 *
 * @author matrix
 * @date 2022/08/27
 */
@Aspect
@Component
@Slf4j
public class InterfaceAccessRestrictionAspect {


    @Autowired
    private StringRedisTemplate redisTemplate;

    @Pointcut("@annotation(interfaceAccessRestriction)")
    public void controllerAspect(InterfaceAccessRestriction interfaceAccessRestriction) {

    }

    @Around(value = "controllerAspect(interfaceM)", argNames = "pjp,interfaceM")
    public Object doAround(ProceedingJoinPoint pjp, InterfaceAccessRestriction interfaceM) throws Throwable {
        //这里用的是 args[0] 作为key,正常场景可以用ip。
        String key = String.valueOf(pjp.getArgs()[0]);
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        if (sra == null){
            return pjp.proceed();
        }
        HttpServletRequest request = sra.getRequest();
        String requestUri = request.getRequestURI();
        String remoteAddr = request.getRemoteAddr();


        BoundHashOperations<String, Object, Object> bho = redisTemplate.boundHashOps("blade:interfaceAccessRestriction:" + key);
        String ipCnt = (String) bho.get(key);
        int uCount = ipCnt == null ? 0 : "".equals(ipCnt) ? 0 : Integer.parseInt(ipCnt);
        if (uCount >= interfaceM.value()) {
            log.error("接口拦截:{} 请求超过限制频率【{}次/{}ms】,参数key为{}", request.getRequestURI(), interfaceM.value(), interfaceM.time(), key);
            R<?> r = new R<>();
            r.setCode(ResultCode.FAILURE.getCode());
            r.setMsg("刷新频率过高,请" + bho.getExpire() + "秒后再操作");
            return r;
        } else {
            bho.increment(key, 1);
            bho.expire(interfaceM.time(), TimeUnit.MILLISECONDS);
        }
        return pjp.proceed();

    }

}

 

 

4、使用

 

 

5、效果

 

 

 

 

再结合自己的需求改改吧。

 

 借鉴博客:https://www.cnblogs.com/zys2019/p/16328053.html#_label0

 

posted @ 2022-08-27 14:09  雁书几封  阅读(1067)  评论(0编辑  收藏  举报