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

来源:互联网 发布:lun网络用语是什么意思 编辑:程序博客网 时间:2024/06/05 14:54

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

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

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

/** *  * @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@Componentpublic 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,然后再返回错误信息。

阅读全文
0 0
原创粉丝点击