项目背景

前段时间公司服务器被攻击,防火墙显示一个南京的ip一天访问接口20w+次,一下子把数据库cpu搞到00%,临时解决办法是把该ip通过防火墙访问策略禁止访问了,为了防止后期还有恶意攻击,我们在项目中新增一个拦截器,根据用户访问的ip和资源请求的url限制其访问频率。

项目介绍

通过拦截器,我们设置了1分钟内同一个ip和ur如果请求超过25次就提醒超过了限定的次数

springboot项目通过ip和url限制用户请求次数_tcp/ip

 

springboot项目通过ip和url限制用户请求次数_网络_02

 

创捷拦截器

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;
}
}

注册拦截器

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);
}

}

Cache缓存类

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;
}
}

}

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>
<!-- 日志 -->