美文网首页终端研发部
如何限制用户在某一时间段多次访问接口

如何限制用户在某一时间段多次访问接口

作者: 风间影月 | 来源:发表于2017-11-09 20:53 被阅读430次

    要知道,如今很多平台的接口都是可以同时被门户网站,手机端,移动浏览器访问,因为接口是通用的,而为了安全起见,有些接口都会设置一个门槛,那就是限制访问次数,也就是在某一时间段内不能过多的访问,比如登录次数限制,在一些金融理财或者银行的接口上比较常见,另外一些与用户信息有关的接口都会有一个限制门槛

    那么这个限制门槛怎么来做呢,其实有很多种方法,主流的做法可以用拦截器或者注解,那么今天咱们用注解来实现

    首先需要定义一个注解,如下:

    /**
     * 
     * @Title: LimitIPRequest.java
     * @Package com.agood.bejavagod.component
     * @Description: 限制某个IP在某个时间段内请求某个方法的次数
     * Copyright: Copyright (c) 2016
     * Company:Nathan.Lee.Salvatore
     * 
     * @author leechenxiang
     * @date 2016年12月14日 下午8:16:49
     * @version V1.0
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    @Documented
    @Order(Ordered.HIGHEST_PRECEDENCE)        // 设置顺序为最高优先级
    public @interface LimitIPRequest {
        
        /**
         * 
         * @Description: 限制某时间段内可以访问的次数,默认设置100
         * @return
         * 
         * @author leechenxiang
         * @date 2016年12月14日 下午8:22:29
         */
        int limitCounts() default 100;
    
        /**
         * 
         * @Description: 限制访问的某一个时间段,单位为秒,默认值1分钟即可
         * @return
         * 
         * @author leechenxiang
         * @date 2016年12月14日 下午8:21:59
         */
        int timeSecond() default 60;
        
    }
    

    然后再使用spring aop,拦截被你注解的那个controller的方法

    @Aspect
    @Component
    public class LimitIPRequestDisplay {
    
        @Autowired
        private JedisClient jedis;
        
        @Pointcut("execution(* com.agood.bejavagod.controller.*.*(..)) && @annotation(com.agood.bejavagod.component.LimitIPRequest)")
        public void before(){
        }
    
        @Before("before()")
        public void requestLimit(JoinPoint joinPoint) throws LimitIPRequestException {
            try {
                // 获取HttpRequest
                ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                HttpServletRequest request = attributes.getRequest();
                HttpServletResponse response = attributes.getResponse();
                
                // 判断request不能为空
                if (request == null) {
                    throw new LimitIPRequestException("HttpServletRequest有误...");
                }
                
                LimitIPRequest limit = this.getAnnotation(joinPoint);
                if(limit == null) {  
                    return;  
                }  
                
                String ip = request.getRemoteAddr();
                String uri = request.getRequestURI().toString();
                String redisKey = "limit-ip-request:" + uri + ":" + ip;
                // 设置在redis中的缓存,累加1
                long count = jedis.incr(redisKey);
                
                // 如果该key不存在,则从0开始计算,并且当count为1的时候,设置过期时间
                if (count == 1) {
                    jedis.expire(redisKey, limit.timeSecond());
    //                redisTemplate.expire(redisKey, limit.time(), TimeUnit.MILLISECONDS);
                }
                
                // 如果redis中的count大于限制的次数,则报错
                if (count > limit.limitCounts()) {
                    // logger.info("用户IP[" + ip + "]访问地址[" + url + "]超过了限定的次数[" + limit.count() + "]");
                    if (ShiroFilterUtils.isAjax(request)) {
                        HttpServletResponse httpServletResponse = WebUtils.toHttp(response);  
                        httpServletResponse.sendError(ShiroFilterUtils.HTTP_STATUS_LIMIT_IP_REQUEST);
                    } else {
                        throw new LimitIPRequestException();
                    }
                }
            } catch (LimitIPRequestException e) {
                throw e;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        /**
         * 
         * @Description: 获得注解
         * @param joinPoint
         * @return
         * @throws Exception
         * 
         * @author leechenxiang
         * @date 2016年12月14日 下午9:55:32
         */
        private LimitIPRequest getAnnotation(JoinPoint joinPoint) throws Exception {  
            Signature signature = joinPoint.getSignature();  
            MethodSignature methodSignature = (MethodSignature) signature;  
            Method method = methodSignature.getMethod();  
      
            if (method != null) {  
                return method.getAnnotation(LimitIPRequest.class);  
            }  
            return null;  
        }  
    }
    

    这个类使用了redis缓存作为计数器,因为好用,当然你用静态的map也行,但是考虑的分布式集群的话一般还是建议使用redis比较好。

    大致的流程就是要获取redis中的调用方法次数,使用incr函数,当key不存在的时候默认为0然后累加1,当累加1大于limit设置的限制次数时,则抛出异常,这个地方需要注意,如果是ajax调用的话需要判断是否ajax,然后再返回错误信息


    image

    查看redis中key的剩余时间:


    image

    好,那么按照如上方法就能实现对接口访问次数的限制

    尚自习 | 程序员的进阶平台 itzixi.com

    微信公众号:BeJavaGod

    Java技术交流群

    相关文章

      网友评论

        本文标题:如何限制用户在某一时间段多次访问接口

        本文链接:https://www.haomeiwen.com/subject/wowmmxtx.html