Implementación de limitación de tasa por dirección MAC con Redis en Java

Enfoque de la solución La dirección MAC del usuario se utiliza como clave única en Redis. En cada solicitud, se verifica el tiempo de vida (TTL) restante de la clave. Si es mayor que cero, se incrementa el contador usando el comando INCR y se compara con el límite de solicitudes permitido. Si la clave no existe o ha expirado, se inicializa un nuevo contador con un tiempo de expiración definido.

Componente de conexión a Redis La siguiente clase gestiona el pool de conexiones a Redis y proporciona operaciones básicas necesarias para la limitación de tasa.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import javax.annotation.PostConstruct;

@Component
public class RedisConnector {

    private static final Logger log = LoggerFactory.getLogger(RedisConnector.class);
    private static JedisPool connectionPool;

    @Value("${redis.host}")
    private String host;

    @Value("${redis.auth}")
    private String password;

    @PostConstruct
    public void initializePool() {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(1200);
        config.setMaxIdle(500);
        config.setMaxWaitMillis(80000);
        config.setTestOnBorrow(true);
        
        connectionPool = new JedisPool(config, host, 6379, 150000, password);
    }

    public long getRemainingTime(String redisKey) {
        Jedis jedisInstance = null;
        long ttlValue = -2;
        try {
            jedisInstance = connectionPool.getResource();
            ttlValue = jedisInstance.ttl(redisKey);
        } catch (Exception ex) {
            log.error("Error obteniendo TTL: {}", ex.getMessage());
        } finally {
            releaseConnection(jedisInstance);
        }
        return ttlValue;
    }

    public void storeWithExpiry(String redisKey, String data, int seconds) {
        Jedis jedisInstance = null;
        try {
            jedisInstance = connectionPool.getResource();
            jedisInstance.setex(redisKey, seconds, data);
        } catch (Exception ex) {
            log.error("Error almacenando clave: {}", ex.getMessage());
        } finally {
            releaseConnection(jedisInstance);
        }
    }

    public long incrementCounter(String redisKey) {
        Jedis jedisInstance = null;
        long result = 0;
        try {
            jedisInstance = connectionPool.getResource();
            result = jedisInstance.incr(redisKey);
        } catch (Exception ex) {
            log.error("Error incrementando contador: {}", ex.getMessage());
        } finally {
            releaseConnection(jedisInstance);
        }
        return result;
    }

    private void releaseConnection(Jedis jedisResource) {
        if (jedisResource != null) {
            connectionPool.returnResource(jedisResource);
        }
    }
}

Lógica de control de acceso El siguiente método determina si una dirección MAC puede realizar una solicitud dentro de un período de teimpo específico según los límites configurados.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AccessControlService {

    private static final Logger logger = LoggerFactory.getLogger(AccessControlService.class);
    private static final String KEY_PREFIX = "access_count:";

    @Autowired
    private RedisConnector redisConnector;

    public boolean checkRequestAllowed(String deviceMac, int windowSeconds, int maxRequests) {
        String accessKey = KEY_PREFIX + deviceMac;
        boolean permitted = false;

        try {
            long timeRemaining = redisConnector.getRemainingTime(accessKey);

            if (timeRemaining > 0) {
                long currentCount = redisConnector.incrementCounter(accessKey);
                
                if (currentCount <= maxRequests) {
                    permitted = true;
                } else {
                    logger.warn("Límite excedido para clave {}: {} solicitudes en {} segundos",
                            accessKey, currentCount, windowSeconds);
                }
            } else {
                redisConnector.storeWithExpiry(accessKey, "1", windowSeconds);
                permitted = true;
            }
        } catch (Exception e) {
            logger.error("Error en verificación de acceso: {}", e.getMessage());
        }

        return permitted;
    }
}

Ejemplo de utilización Para restringir a 8 solicitudes por cada 45 minutos para una dirección MAC específica:

@Autowired
private AccessControlService accessControl;

public void handleRequest(String macAddress) {
    if (accessControl.checkRequestAllowed(macAddress, 2700, 8)) {
        // Procesamiento de la solicitud permitida
    } else {
        // Respuesta de límite de tasa excedido
    }
}

Etiquetas: Redis Jedis Frecuencia de acceso Limitación de tasa java

Publicado el 6-2 17:12