我试图阻止某些 IP 地址和 IP 范围执行某些操作。现在,SELECT * FROM blocked WHERE ip=$_SERVER['REMOTE_ADDR']
如果 ->num_rows 返回 > 0,我将重定向用户。
这适用于完整的 IP 地址,但如果我将199.90.*.*
例如放入数据库中,我该如何匹配呢?另外,将IP存储在数据库中的最佳方法是什么?
我试图阻止某些 IP 地址和 IP 范围执行某些操作。现在,SELECT * FROM blocked WHERE ip=$_SERVER['REMOTE_ADDR']
如果 ->num_rows 返回 > 0,我将重定向用户。
这适用于完整的 IP 地址,但如果我将199.90.*.*
例如放入数据库中,我该如何匹配呢?另外,将IP存储在数据库中的最佳方法是什么?
您的问题的答案是使用like
:
SELECT *
FROM blocked
WHERE $_SERVER['REMOTE_ADDR'] like replace(ip, '*', '%')
中的通配符like
是'%'
而不是'*'
,所以这用百分号替换了星号。我猜您实际上会将字符串存储为'199.199.%.%'
,但上面是为了清楚起见。
虽然这在技术上解决了您的问题,但它还有其他问题,因为 MySQL 可能不使用索引进行这些比较(请参阅此处)。影响取决于阻塞表的大小。如果它有 100 行,这可能不是问题。如果它有 100,000,那么它可能是。
对阻塞表使用通配符的另一种方法是将FromIP
和ToIP
作为列。然后像“199.199.. ”这样的东西将简单地存储为“199.199.000.000”和“199.199.999.999”。查询将是:
where $_SERVER['REMOTE_ADDR'] between FromIP and ToIP
你会有一个关于blocked(FromIP, ToIP)
.
您最好的解决方案是使用INET_ATON函数。
给定 IPv4 网络地址作为字符串的点分四元组表示,返回一个整数,该整数表示网络字节顺序(大端)地址的数值。
因此,如果您想查找某个范围内的 IP,您可以使用如下查询:
SELECT * FROM blocked WHERE INET_ATON($_SERVER['REMOTE_ADDR']) between INET_ATON(this_ip) and INET_ATON(this_other_ip).
我认为,在 MySQL 数据库中存储 IPv4 地址的最佳方法是使用该函数,将它们转换为一个int
值并像这样存储它。如果您想从它的int
表示中获取 IP,请使用INET_NTOA。
将 IP 存储为int
,查询结束如下:
SELECT * FROM blocked WHERE INET_ATON($_SERVER['REMOTE_ADDR']) between this_ip and this_other_ip.
而不是使用 199.90.*.* 使用 (199.90.0.0)。
我将首先检查完整的 IP 地址,然后用 * 屏蔽最后一个字节,然后检查它,然后检查最后 2 个字节,依此类推。这样一来,您就可以从特定到不太特定,如果一旦命中,IP就会被阻止。这可能不是最有效的方法,但我想您只需在登录时执行一次。
为简单起见,我会将 IP 地址存储为字符串。
另一种解决方案可能是将 IP 地址存储为字节数组,但我猜这不会让任何事情变得更容易,并且使用例如命令行工具 SQL 工具的可维护性会大大降低。