El proyecto springboot limita la cantidad de solicitudes de usuarios a través de IP y URL

Antecedentes del proyecto

Hace algún tiempo, el servidor de la empresa fue atacado. El firewall mostró que una IP de Nanjing accedía a la interfaz más de 200.000 veces al día, lo que de repente llevó la CPU de la base de datos al 00%. La solución temporal fue prohibir el acceso a la IP a través del firewall. política, para evitar comportamientos maliciosos en el futuro. Para atacar, agregamos un nuevo interceptor al proyecto para limitar la frecuencia de acceso en función de la IP a la que accede el usuario y la URL de la solicitud de recurso.

Introducción al proyecto

A través del interceptor, configuramos un recordatorio de que si se solicita la misma IP y UR más de 25 veces en 1 minuto, se ha excedido el límite.

 

 

Interceptor Chuangjie

package com.brickdog.filter;


import com.brickdog.common.utils.CacheUtils;
import com.brickdog.common.utils.RequestLimit.HttpUtil;
import com.brickdog.common.utils.RequestLimit.RequestLimitException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;



@Slf4j
@Component
public class RequestLimitInterceptor implements HandlerInterceptor {


    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws RequestLimitException {
        try {


            Integer limit_count = 25;
            Integer limit_time = 1000 *60;

            String ip = HttpUtil.getIpByRequest(httpServletRequest);
            String url = httpServletRequest.getRequestURL().toString();
            String key = "req_limit_".concat(url).concat(ip);

            String cache = (String)CacheUtils.get(key);
            if (null == cache) {
                String value = "1_" + System.currentTimeMillis();
                CacheUtils.put(key,value,limit_time);
            } else {
                String value = (String) cache;
                String[] s = value.split("_");
                int count = Integer.parseInt(s[0]);

                if (count > limit_count) {
                    log.info("用户IP[{}], 访问地址[{}], 超过了限定的次数[{}]", ip, url, limit_count);
                    throw new RequestLimitException();
                }

                value = (count + 1) + "_" + s[1];
                long last = limit_time - (System.currentTimeMillis() - Long.parseLong(s[1]));
                if (last > 0) {
                    CacheUtils.put(key,value,limit_time);
                }
            }


        } catch (RequestLimitException e) {
            throw e;
        } catch (Exception e) {
            log.error("发生异常", e);
        }
        return  true;
    }
}

Registrar interceptor

package com.brickdog.config;


import com.brickdog.filter.AuthHandlerInterceptor;
import com.brickdog.filter.RequestLimitInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author brickdog
 * @version 1.0
 * @create 2022/2/4 21:24
 */
@Configuration
public class AuthConfigurer implements WebMvcConfigurer {

    @Autowired
    RequestLimitInterceptor requestLimitInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(requestLimitInterceptor);
    }

}

clase de caché de caché

package com.brickdog.common.utils;

import java.util.Map;
import java.util.concurrent.*;

public class CacheUtils {

    // 键值对集合
    private final static Map<String, Entity> map = new ConcurrentHashMap<>();
    // 定时器线程池, 用于清除过期缓存
    private final static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

    /**
     * 添加缓存
     */
    public synchronized static void put(String key, Object data) {
        CacheUtils.put(key, data, 0);
    }

    /**
     * 添加缓存
     * 过期时间: 单位毫秒, 0表示无限长
     */
    public synchronized static void put(String key, Object data, long expire) {
        // 清除原键值对
        CacheUtils.remove(key);
        // 设置过期时间
        if (expire > 0) {
            Future future = executor.schedule(() -> {
                // 过期后清除该键值对
                synchronized (CacheUtils.class) {
                    map.remove(key);
                }
            }, expire, TimeUnit.MILLISECONDS);
            map.put(key, new Entity(data, future));
        } else {
            // 不设置过期时间
            map.put(key, new Entity(data, null));
        }
    }

    /**
     * 读取缓存
     */
    public synchronized static Object get(String key) {
        Entity entity = map.get(key);
        return entity == null ? null : entity.getValue();
    }

    /**
     * 读取缓存
     * clazz 值类型
     */
    public synchronized static <T> T get(String key, Class<T> clazz) {
        return clazz.cast(CacheUtils.get(key));
    }

    /**
     * 清除指定缓存
     * 返回值为指定key的value
     */
    public synchronized static Object remove(String key) {
        // 清除指定缓存数据
        Entity entity = map.remove(key);
        if (entity == null)
            return null;
        // 清除指定键值对定时器
        Future future = entity.getFuture();
        if (future != null)
            future.cancel(true);
        return entity.getValue();
    }

    /**
     * 清除所有缓存
     */
    public synchronized static void removeAll() {
        map.clear();
    }

    /**
     * 查询当前缓存的键值对数量
     */
    public synchronized static int size() {
        return map.size();
    }

    /**
     * 缓存实体类
     */
    private static class Entity {
        // 键值对的value
        private Object value;
        // 定时器的future
        private Future future;

        /**
         * 创建实体类
         */
        public Entity(Object value, Future future) {
            this.value = value;
            this.future = future;
        }

        /**
         * 获取value值
         */
        public Object getValue() {
            return value;
        }

        /**
         * 获取future对象
         */
        public Future getFuture() {
            return future;
        }
    }

}

coordenadas pom

<!-- 日志 -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.30</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.16</version>
</dependency>
<!-- 日志 -->

Supongo que te gusta

Origin blog.csdn.net/qq243348167/article/details/126072773
Recomendado
Clasificación