如何限制用戶在某一時間段多次訪問接口


要知道,如今很多平台的接口都是可以同時被門戶網站,手機端,移動瀏覽器訪問,因為接口是通用的,而為了安全起見,有些接口都會設置一個門檻,那就是限制訪問次數,也就是在某一時間段內不能過多的訪問,比如登錄次數限制,在一些金融理財或者銀行的接口上比較常見,另外一些與用戶信息有關的接口都會有一個限制門檻

那么這個限制門檻怎么來做呢,其實有很多種方法,主流的做法可以用攔截器或者注解,那么今天咱們用注解來實現

首先需要定義一個注解,如下:

/**
 * 
 * @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,然后再返回錯誤信息

查看redis中key的剩余時間:

好,那么按照如上方法就能實現對接口訪問次數的限制


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM