天天看点

使用注释AOP实现接口访问次数限制

前言

在日常开发工作中,我们常有接口会暴露出来,虽然我们增加了各种检验和拦截可以拦截大多数恶意访问,但是你不能保证对接方的猿子不会造出一个死循环来访问你的接口,尤其是我们的程序作为一个平台使用的时候,别人的一个误操作可能会造成服务器宕机,到时候成千上万的客户都会受到影响,所以在这种对接过程中一定要对对方的接口访问次数进行限制!这种方式可以理解为微服务中的服务降级!

安排栗子

新建一个注释类:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LimitTime {
    // 访问次数,默认为10次
    int time() default 10;
    // 过期时间,时间戳间隔
    long timeout() default 1;
}      

定义一个存放调用信息的DTO:

@Data
public class LimitDTO {
    //最近一次刷新时间戳
    private Long refreshTime;
    //剩余访问次数
    private Integer time;
}      

新建一个切面类:

注意在存储访问状态对象的时候一定要使用ConcurrentHashMap,此为线程安全的map,支持并发访问!

@Component
@Order
@Aspect
public class LimitTimeAspect {
    private ConcurrentHashMap<String, LimitDTO> limitMap = new ConcurrentHashMap<>();

    @Pointcut("@annotation(limitTime)")
    public void limit(LimitTime limitTime) {
    }

    @Around("limit(limitTime)")
    public Object aroundLog(ProceedingJoinPoint joinpoint, LimitTime limitTime) throws Throwable {
        //获取传入的最大访问次数
        int time= limitKey.time();
        //获取计算时间
        long timeout = limitKey.timeout();
        //获取访问方法
        Object target = joinpoint.getTarget().getClass().getName();
        String key= target.toString();
        //如果第一次访问该方法
        if (limitMap.get(key) == null) {
            //新建一次对象存放访问信息
            LimitDTO limitDTO=new LimitDTO();
            limitDTO.setTime(time- 1);
            limitDTO.setRefreshTime(new Date().getTime());
            limitMap.put(key, limitDTO);
        }else {
            //如果不是第一次访问,获取上次访问的信息
            LimitDTO limitDTO=limitMap.get(key);
            //如果和上次刷新时间比已经过期
            if (new Date().getTime() - limitDTO.getRefreshTime() > timeout) {
                //将对象中的刷新时间和访问次数刷新
                limitDTO.setRefreshTime(new Date().getTime());
                limitDTO.setTime(time);
                limitMap.put(key, limitDTO);
            }
            //获取当前访问对象中的剩余访问次数
            int t = (int) limitMap.get(key).getTime;
            //如果访问次数大于0
            if (t > 0) {
                //允许访问,并将访问次数-1
                limitDTO.setTime(--t);
            } else {
                //如果已经没有访问次数,返回错误信息
                ResultBO<Object> resultBO = new ResultBO<>();
                resultBO.setCode(1);
                resultBO.setMsg("已达最大访问次数");
                return resultBO;
            }
        }
        //打印信息
        System.err.println("剩余次数:" + limitMap.get(key).getTime + " 方法名称:" + key);
        return joinpoint.proceed();
    }

      

使用该注释:

@RequestMapping(value = "/sendCmd", method = RequestMethod.POST)
@ResponseBody
@LimitKey(time= 10, timeout = 10000)//10秒内可以访问10次
public int sendCmd(@RequestBody List<CmdDO> cmds) {
    //do something
    return new ResultBO<>();
}

      

测试:

使用注释AOP实现接口访问次数限制
使用注释AOP实现接口访问次数限制

撒花!完成!

继续阅读