限制每个IP的TCP请求

4

我想知道如何在Java中限制每个客户端(特定IP)的TCP请求。例如,我希望为每个客户端IP允许在Y秒内最多X个请求。我考虑使用静态计时器/计时器任务与临时受限IP的哈希集合相结合。

private static final Set<InetAddress> restrictedIPs = Collections.synchronizedSet(new HashSet<InetAddress>());

private static final Timer restrictTimer = new Timer();

所以当用户连接到服务器时,我将他的IP添加到限制列表中,并启动一项任务,在X秒钟后取消限制。

restrictedIPs.add(socket.getInetAddress());

restrictTimer.schedule(new TimerTask()
{
  public void run()
  {
    restrictedIPs.remove(socket.getInetAddress());
  }

},  MIN_REQUEST_INTERVAL);

我的问题在于,当任务运行时,套接字对象可能已经关闭,远程IP地址将不再可访问...

欢迎任何想法!此外,如果有人知道使用Java框架内置的方法来实现这一点,我很想听听。

2个回答

1

一个选择是使用 netfilter 来完成这个任务。虽然不是“纯”Java,但可能是最强大和最稳定的解决方案。该示例取自debian-administration

iptables -I INPUT -p tcp --dport 80 -i eth0 -m state --state NEW -m recent \
  --set

iptables -I INPUT -p tcp --dport 80 -i eth0 -m state --state NEW -m recent \
  --update --seconds 60 --hitcount 4 -j DROP

编辑:

如果只是快速修复您的代码,请保存IP地址并在阻止之前添加计数,即(伪):

IPaddress addr = socket.getAddress();
int hitcount = hitcountMap.get(addr).value();
if (hitcount <= HIT_MAX) {
    //only increase if person hasn't reached roof, prevents 'overblocking'
    hitcountMap.get(addr).increase();
    unblockTimer.schedule(hitcountMap.get(addr).decrease(), BLOCK_TIMEOUT);
}
if (hitcount > HIT_MAX) {
    connection.drop();
}

这应该使块最多持续 BLOCK_TIMEOUT(不能保证无错误)。考虑使用信号量进行计数,因为您将(可能)从许多线程中运行此代码,当然取决于您的线程模型。


hitcount.get?hitcount不是整数吗? - user356178
哦,我使用hitcount来表示一个map<addr,int>和我从中得到的int。有点混淆,会改变的。 - Alexander Torstling
谢谢!不幸的是,我不能使用iptables,因为我的服务器应用程序需要跨平台支持。但是,hitcount的想法不错。 - user356178

0
一个简单的解决方案是:
final InetAddress ip = socket.getInetAddress();
restrictedIPs.add(ip);

restrictTimer.schedule(new TimerTask()
{
  public void run()
  {
    restrictedIPs.remove(ip);
  }

},  MIN_REQUEST_INTERVAL);

这种方法的问题在于需要额外的线程来删除受限制的IP地址。也许更容易的方法是保持一个InetAddress到时间戳的映射,以跟踪它们上次访问页面的时间。这样,每次客户端开始请求时,您只需检查此时间戳即可。

谢谢你的回答!我可能会按照你说的使用 Map。 - user356178
这个解决方案只有一个问题:如何确定何时从地图中删除IP?因为如果服务器运行时间很长,它最终会拥有数千个IP的地图。 - user356178
几千个应该不是问题,但我理解你的担忧。你可以安排每隔一段时间进行清理,删除所有旧的IP(比如超过一个小时的)。 - Marc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接