温馨提示:
本文所述内容具有依赖性,可能因软硬条件不同而与预期有所差异,故请以实际为准,仅供参考。
在做安全访问限制时,有时候我们需要做 IP 黑名单或者白名单,为减小策略长度,提高效率,比较常见的用法就是对该 IP 段内的所有 IP 进行允许或者限制,由此带来一个问题,就是我们如何判断某个 IP 是否属于某个 IP 段内。
判断一个 IP 是否属于某一个 IP 段,思路就是将 varchar
类型的 IP 转换成 int
类型的数值,然后进行比较。在 PHP 中内置
了 ip 和数值的互换的函数:ip2long($ip_addr)
和 long2ip($long)
。
ip2long($ip_addr)
中若 $ip_addr
不是有效的 IP 地址,则会返回 false
,若是有效的 IP 地址,则会将其转换为有符号 long 类型
,而有些时候 IP 地址转换后可能会超出有符号的 long 长度,这样会导致出现负数。
long 类型是 4 个字节的,其有符号范围是:2147483647 ~ -2147483648
,无符号的范围是:4294967295 ~ 0
。
最大的 IP4 地址是:255.255.255.255
,对应的数值是:4294967295
,所以可以将其转成无符号的数值。2147483647
数值对应的 IP 为:127.255.255.255
,所以 IP 地址在 0.0.0.0
至 127.255.255.255
范围内的不会出现负数,而在 128.0.0.0 ~ 255.255.255.255
范围内的会出现负数情况。
以下代码给出了一个完整、正确的判断指定 IP 是否属于某 IP 段内的小例子:
$start_ip = "42.236.184.1";
$end_ip = "42.236.184.255";
$ip = "42.236.184.128";
function check_ip($ip,$start_ip,$end_ip)
{
$ip = get_ip2long($ip);
$start_ip = get_ip2long($start_ip);
$end_ip = get_ip2long($end_ip);
if($ip >= $start_ip && $ip <= $end_ip)
{
return true;
}
return false;
}
function get_ip2long($ip)
{
return bindec(decbin(ip2long($ip)));
// 方法二:return sprintf('%u',ip2long($ip)); // %u:不包含正负号的十进制数
// decbin() 十进制转二进制。
// bindec() 二进制转十进制。函数将一个二进制数转换成 integer。可转换的最大的数为 31 位 1 或者说十进制的 2147483647。PHP 4.1.0 开始,该函数可以处理大数值,这种情况下,它会返回 float 类型。
}
check_ip($ip,$start_ip,$end_ip);
其实本文还有一个应用场景,就是通常存储在数据库中的 IP 地址都是 varchar 类型的,而如果采用 INT 类型来保存 IP ,则可以对数据库起到优化的作用。
参考文章:
1、《PHP 判断某个 IP4 是否属于某一个 IP 段(ip2long())》