Redis AOP 接口限流 時間單位請求次數

前言

今天項目需要對接口進行限制訪問次數(某個時間段只允許訪問幾次) 項目經理說是做成一個AOP 小組件的形式,便於以後其他項目複用,這次我們是用Redis+AOP 實現的。

實現邏輯:

使用請求頭中的userid 來作爲key 登陸次數作爲value
請求時,判斷Redis中是否有key ,if(沒有) {新增key+value+定時時間}
if(有){判斷value是否超出規定次數 if(超出){異常返回} if(未超出){ value+1 更新數據}}

1.自定義註解類

/**
 * @Description:  接口限流自定義註解
 * @author: xuxinku
 * @Date: 2020/4/6 14:18
 * @ModifiedDate:
 * @Copyright:XX保險股份有限公司
 */
@Documented
@Target(ElementType.METHOD) //代表方法級別
@Inherited //是指定某個自定義註解如果寫在了父類的聲明部分,那麼子類的聲明部分也能自動擁有該註解
@Retention(RetentionPolicy.RUNTIME)
public @interface AccessRestriction {


    /**
     * 多長時間內限制,默認 60
     * @return
     */
    long t () default 60;

    /**
     * 單位時間內能訪問多少次,默認3次
     * @return
     */
    int count () default 3;

}

2.切面類實現

/**
 * @Description:  接口限流使用
 * @author: xuxinku
 * @Date: 2020/4/6 14:18
 * @ModifiedDate:
 * @Copyright:江泰保險股份有限公司
 */
@Aspect
@Component
@Slf4j
public class AccessRestrictionAop {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;


    @Pointcut("@annotation(com.jtpf.es.aop.AccessRestriction)")
    public void executeService() {
    }

    @Before("executeService()&&@annotation(accessRestriction)")
    public void checkIP(JoinPoint joinPoint, AccessRestriction accessRestriction)throws Exception  {
        // 規則:不存在 賦初始值/存在且大於 拋出超時異常/ 次數+1
        String key = LoginAop.getUserCode();
        //根據key  查詢是否存在  hasKey
        String oldKay = redisTemplate.opsForValue().get(key);

        // 不存在 賦初始值
        if(StringUtils.isEmpty(oldKay)){
            redisTemplate.opsForValue().set(key, "0", accessRestriction.t(), TimeUnit.SECONDS );
        }

        //存在且大於規定次數
        int frequency = Integer.valueOf(redisTemplate.opsForValue().get(key)).intValue();
        if (!StringUtils.isEmpty(oldKay) && frequency > accessRestriction.count()){
            throw new BizException("當前用戶已請求次數過多 請稍後重試");
        }
        //次數+1
        int i = frequency +1;
        //更新記錄次數
        redisTemplate.opsForValue().set(key, i+"",0 );


        log.info("開始記錄操作信息 ");
        System.out.println("請求次數=====》"+oldKay);
        System.out.println("key=====》"+key);
        log.info("記錄操作信息成功");
    }

3.LoginAop工具

/**
 * 必須添加這兩個註解:Component: 泛指組件,講該類納入bean中;
 * Aspect: 通過該註解及表達式就可以輕鬆的使用POJO來定義切面
 * * @author tdh
 */
@Aspect
@Slf4j
@Component
public class LoginAop {


    /**
     * 獲取用戶編碼
     *
     * @return
     */
    public static String getUserCode() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String userCode = request.getHeader("jtpf.userId");
        if (userCode == null) {
            userCode = request.getParameter("jtpf.userId");
        }

        if (StringUtils.isBlank(userCode)) {
            throw new ParamException("請重新登陸");

        } else {
            return userCode;
        }
    }
    /**
     * 獲取分支機構編碼
     *
     * @return
     */
    public static String getBranchCode() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        if (StringUtils.isNotBlank(request.getHeader("branchCode"))) {
            return URLDecoder.decode(request.getHeader("branchCode"));
        } else {
            return null;
        }
    }

    /**
     * 獲取總公司機構編碼
     *
     * @return
     */
    public static String getParentCode() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        if (StringUtils.isNotBlank(request.getHeader("parentCode"))) {
            return URLDecoder.decode(request.getHeader("parentCode"));
        } else {
            return null;
        }
    }

    /**
     * 獲取用戶名稱
     *
     * @return
     */
    public static String getUserName() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        if (StringUtils.isNotBlank(request.getHeader("userName"))) {
            return URLDecoder.decode(request.getHeader("userName"));
        } else {
            return null;
        }
    }

    /**
     * 定義攔截規則:攔截標有com.SheetChrist.annotation.Login類中註解的所有方法com.flow.controller.FlowController
     */
    @Pointcut("execution(public * com.*.controller.*.*(..))")
    public void loginMethodPointcut() {
    }

    @Around("loginMethodPointcut()")
    public Object interceptor(ProceedingJoinPoint point) throws Throwable {
        //返回的結果
        Object result = null;
        result = point.proceed();
        return result;
    }
    public static String getIpAddr(){
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        if ("0:0:0:0:0:0:0:1".equals(ip)) {
            ip = "127.0.0.1";
        }
        if (ip.split(",").length > 1) {
            ip = ip.split(",")[0];
        }
        return ip;
    }

}

4.註解的使用

60秒 5次

    @AccessRestriction(t =60, count = 5)
    @GetMapping(value = "/testAOP2")
    public void testAop2() {

        System.out.println("限流測試===========結束》");

    }

測試:
在這裏插入圖片描述在這裏插入圖片描述


每天記錄發現的問題,不進步就是在退步

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