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("限流测试===========结束》");

    }

测试:
在这里插入图片描述在这里插入图片描述


每天记录发现的问题,不进步就是在退步

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