如何修改PHP IP归属判断代码适配非学校服务器并展示对应提示
解决方案
原代码失效原因
原有代码是托管在学校内部服务器时的逻辑:当时校内访客访问时拿到的是10开头的内网IP,校外访客拿到公网IP;站点迁到外部托管平台后,所有访客的请求IP都是公网IP,不会再出现10开头的内网地址,原有判断逻辑自然失效。
前置准备
- 联系学校网络管理部门,获取学校所有公网出口的IP段,建议要CIDR格式的完整段(比如
131.125.0.0/16、202.115.xxx.0/24这类),如果需要支持IPv6也要同步获取对应段 - 确认托管环境的真实访客IP获取方式:如果站点用了CDN、反向代理,不能直接取
$_SERVER['REMOTE_ADDR'],需要从服务商提供的可信请求头(比如X-Forwarded-For)获取真实IP,注意只信任CDN/代理的官方IP段的请求头,避免攻击者伪造IP。
改进后代码示例
首先写一个通用的IP段匹配函数,支持CIDR格式的IPv4/IPv6判断,替换原来简陋的字符串拆分逻辑:
<?php // 判断IP是否在指定CIDR段内 function ipInCidr(string $ip, string $cidr): bool { // 拆分CIDR为IP和前缀长度 list($cidrIp, $prefixLength) = explode('/', $cidr, 2) + [1 => ($ipType = filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) ? 32 : 128]; // 校验IP格式 if (!filter_var($ip, $ipType ? FILTER_VALIDATE_IP : FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { return false; } if (!filter_var($cidrIp, $ipType ? FILTER_VALIDATE_IP : FILTER_VALIDATE_IP)) { return false; } // 转换为二进制字符串比对 $ipBin = inet_pton($ip); $cidrIpBin = inet_pton($cidrIp); $mask = str_repeat('f', $prefixLength / 4) . str_repeat('0', ($ipType ? 32 : 128) - $prefixLength); $mask = pack('H*', $mask); return ($ipBin & $mask) === $cidrIpBin; } // 替换为你从学校拿到的全部公网IP段 $schoolIpRanges = [ '131.125.0.0/16', // 其他段按这个格式添加 ]; // 这里替换为你环境里获取真实访客IP的逻辑 $visitorIp = $_SERVER['REMOTE_ADDR']; // 如果用了CDN服务,用对应服务商提供的请求头获取真实IP,注意要先校验请求是否来自CDN官方IP // $visitorIp = $_SERVER['HTTP_X_FORWARDED_FOR']; $isFromSchool = false; foreach ($schoolIpRanges as $range) { if (ipInCidr($visitorIp, $range)) { $isFromSchool = true; break; } } if ($isFromSchool) { echo "<br>You are from XXX domain."; } else { echo "<br>You are not from XXX domain."; }
注意事项
- 学校的公网IP段如果有调整,要同步更新代码里的
$schoolIpRanges列表,避免判断出错 - 不要直接使用未校验的
X-Forwarded-For头获取IP,攻击者可以伪造这个头的内容绕过IP判断 - 如果无法拿到学校官方IP段,可以用商用IP归属地库做补充判断,但精准度远低于官方提供的IP段
内容的提问来源于stack exchange,提问作者knowledge of no knowledge
相关产品推荐
相关产品推荐

