限制接口的訪問次數


情景:發送手機驗證碼或者郵箱驗證碼時限制規則:一分鍾只可以發一次,一天內也有次數限制。以防止惡意訪問,降低服務器壓力。

解決思路:獲取用戶ip地址,判斷此ip是否首次訪問,如果是首次訪問,在redis創建minKey,dayKey.並設置minKey過期60s,dayKey為86400s,也就是24H。首次訪問則次數加一。超過1次或一天超過限制次數時,禁止訪問。

1.獲取用戶真實ip地址。

/**
 * 自定義訪問對象工具類
 * 獲取對象的IP地址等信息 
 */
public class GetIpAddressUtil {
    /**
     * 獲取用戶真實IP地址,不使用request.getRemoteAddr();的原因是有可能用戶使用了代理軟件方式避免真實IP地址,
     * 
     * 可是,如果通過了多級反向代理的話,X-Forwarded-For的值並不止一個,而是一串IP值,究竟哪個才是真正的用戶端的真實IP呢?
     * 答案是取X-Forwarded-For中第一個非unknown的有效IP字符串。
     * 
     * 如:X-Forwarded-For:192.168.1.110, 192.168.1.120, 192.168.1.130,
     * 192.168.1.100
     * 用戶真實IP為: 192.168.1.110
     * @param request
     * @return
     */
    public static String getIpInfo(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            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.getHeader("HTTP_CLIENT_IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
        } else if (ip.length() > 15) {
            String[] ips = ip.split(",");
            for (int index = 0; index < ips.length; index++) {
                String strIp = (String) ips[index];
                if (!("unknown".equalsIgnoreCase(strIp))) {
                    ip = strIp;
                    break;
                }
            }
        }
        return ip;
    }
}

2.Redis配置

public class MyJedisPool {
    private final static Logger logger = LoggerFactory.getLogger(MyJedisPool.class);
    private static JedisPool jedisPool = null;
    //redis服務配置
      public static final String Redis_IP = "192.168.xx.xx";
      public static final int Redis_PORT =6379;
      public static final String Redis_jedisUser ="";//密碼
      public static final int ExpireSeconds = 3600;
    //靜態代碼初始化池配置
    static {
        try{
            //創建jedis池配置實例
            JedisPoolConfig config = new JedisPoolConfig();
            //設置池配置項值
            config.setMaxTotal(20);
            config.setMaxIdle(10);
            config.setMaxWaitMillis(3000);
            config.setTestOnBorrow(true);
            config.setTestOnReturn(false);
            //根據配置實例化jedis池
            //jedisPool = new JedisPool(config,Redis_IP,Redis_PORT,1000*2,Redis_jedisUser);
            jedisPool = new JedisPool(config,SystemFileConfig.get("Redis_IP"),
                    Integer.parseInt(SystemFileConfig.get("Redis_PORT")),
                    1000*2,
                    SystemFileConfig.get("Redis_jedisUser"),
                    Integer.parseInt(SystemFileConfig.get("Redis_database")));     
        }catch (Exception e) {
            logger.info("redis連接池異常",e);
        }
    } 
    /**獲得jedis對象*/
    public static Jedis getJedisObject(){
        return jedisPool.getResource();
    }
    
    /**歸還jedis對象*/
    public static void returnJedisOjbect(Jedis jedis){
        if (jedis != null) {
            jedis.close();
        }
    }
}

3.操作redis完成次數限制

String userIpAddr = GetIpAddressUtil.getIpInfo(request);
Jedis jedis = MyJedisPool.getJedisObject();//獲取jedis鏈接對象
boolean flag = true;               
flag = limitSendCount(userIpAddr,jedis);
System.out.println(flag); //根據flag判斷是否超出限制
if (jedis != null) {
    jedis.close();  //用完關閉jedis資源
}                        
//判斷是否超出限制
 public boolean limitSendCount(String ip, Jedis jedis) {     
            String value = jedis.get(ip+"MinLimte");
            String dayValue = jedis.get(ip+"DayLimte");
            if(value==null){
                jedis.set(ip+"MinLimte", "1");
                jedis.expire(ip+"MinLimte", 60);//設置過期時間60秒
                if(dayValue==null) {
                    jedis.set(ip+"DayLimte", "1");
                    jedis.expire(ip+"DayLimte", 86400);//設置過期時間24hours
                }else {
                    jedis.incr(ip+"DayLimte");  //加一次
                    int parseIntDay = Integer.parseInt(dayValue);
                    if(parseIntDay>20){
                        System.out.println("訪問受限!!!!");
                        return false;
                    }
                }
                return true;
            }else{
                int parseInt = Integer.parseInt(value);
                int parseIntDay = Integer.parseInt(dayValue);
                60秒內訪問超過1次,或者一天超過20次,就禁止訪問
                if(parseInt>=1 || parseIntDay>20){
                    System.out.println("訪問受限!!!!");
                    return false;
                }
                jedis.incr(ip+"MinLimte");
                jedis.incr(ip+"DayLimte");
            }            
            return true;
        } 

4.以上后端驗證次數已完成。如果前后不分離,前端需要在點擊按鈕后禁止點擊,按鈕顯示倒計時直至為0s方可再次點擊時,前端js如下:

//發送成功后60秒內不可再次發送
var id = 60;
//定時執行
var timeing = setInterval(function() {
    id = id - 1;
    $('#sendVcode').html(id + 's');
    }, 1000);
//延遲執行
window.setTimeout(function() { //結束定時,恢復按鈕可用 clearInterval(timeing); $('#sendVcode').html('點擊發送Send').removeAttr("disabled"); $("#sendVcode").removeClass("sendclass2");//設置自己的按鈕樣式 $("#sendVcode").addClass("sendclass1"); }, 60000);

 


免責聲明!

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



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