許多系統需依 IP 限制存取權限(如校園內網專屬新聞、後台管理介面)。本文提供兩種實作方案:
ip.txt 存放允許 IP/網段,支援 C 類網段(如 192.168、211.67.188)與單一 IP。block_ip 類別,支援 IP-IP 範圍、* 萬用字元、自動偵測代理 Header(HTTP_X_FORWARDED_FOR、HTTP_CLIENT_IP)並整合正則比對。192.168
211.67.188
211.67.191.25
<?php
$ip = $_SERVER['REMOTE_ADDR'];
// 讀取 ip.txt,移除換行符號
$ipArray = preg_replace("#\r\n?|\n#", "", file('ip.txt'));
foreach ($ipArray as $ipTest) {
// substr_count 判斷客戶端 IP 是否包含名單片段
if (substr_count($ip, $ipTest) !== 0) {
echo "ok"; // 允許存取,執行後續邏輯
exit;
}
}
// 不在名單中 → 拒絕或導向錯誤頁
echo "Access Denied";
?>
原理:利用字串包含關係比對,192.168 即可涵蓋 192.168.x.x 整個 C 類網段,簡單直觀但無法處理複雜 CIDR(如 /24、/16)或 IP 範圍(192.168.1.10-192.168.1.20)。
192.168.1.1(單一)、210.10.2.1-20(範圍)、222.34.4.*(萬用字元)$_SERVER 取得真實客戶端 IP(穿透反向代理/CDN)<?php
class block_ip {
// 封鎖規則陣列,可從資料庫/設定檔載入
var $Block_ip = array("192.168.1.1", "210.10.2.1-20", "222.34.4.*");
function __construct() {}
function __destruct() {}
// 將單一規則轉為正則片段
private function makePregIP($str) {
$preg_limit = '';
if (strstr($str, "-")) {
$aIP = explode(".", $str);
foreach ($aIP as $k => $v) {
if (!strstr($v, "-")) {
$preg_limit .= $this->makePregIP($v);
} else {
$aipNum = explode("-", $v);
$preg = '';
for ($i = $aipNum[0]; $i <= $aipNum[1]; $i++) {
$preg .= $preg ? "|".$i : "[".$i;
}
$preg_limit .= (strrpos($preg_limit, ".") === strlen($preg_limit)-1) ? $preg."]" : ".".$preg."]";
}
}
} else {
$preg_limit .= $str.".";
}
return $preg_limit;
}
// 取得所有編譯後的正則規則
private function getAllBlockIP() {
$ip_list = [];
if ($this->Block_ip) {
foreach ($this->Block_ip as $k => $v) {
$ipaddres = $this->makePregIP($v);
$ip = str_ireplace(".", "\.", $ipaddres);
$ip = str_replace("*", "[0-9]{1,3}", $ip);
$ip_list[] = "/".$ip."/";
}
}
return $ip_list;
}
// 取得客戶端真實 IP
private function get_client_ip() {
if (getenv("HTTP_CLIENT_IP") && strcasecmp(getenv("HTTP_CLIENT_IP"), "unknown"))
$ip = getenv("HTTP_CLIENT_IP");
else if (getenv("HTTP_X_FORWARDED_FOR") && strcasecmp(getenv("HTTP_X_FORWARDED_FOR"), "unknown"))
$ip = getenv("HTTP_X_FORWARDED_FOR");
else if (getenv("REMOTE_ADDR") && strcasecmp(getenv("REMOTE_ADDR"), "unknown"))
$ip = getenv("REMOTE_ADDR");
else if (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], "unknown"))
$ip = $_SERVER['REMOTE_ADDR'];
else
$ip = "unknown";
return $ip;
}
// 公開檢查方法
public function checkIP() {
$iptable = $this->getAllBlockIP();
$IsJoined = true;
$Ip = trim($this->get_client_ip());
if ($iptable) {
foreach ($iptable as $value) {
if (preg_match($value, $Ip)) {
$IsJoined = false;
break;
}
}
}
if (!$IsJoined) {
header("HTTP/1.1 403 Forbidden");
exit("IP Error: Access Denied");
}
}
}
// 使用方式
$oBlock_ip = new block_ip();
$oBlock_ip->checkIP();
// 通過後繼續執行正常業務邏輯
?>
real_ip_header,避免偽造 X-Forwarded-For。makePregIP。makePregIP 邏輯。$IsJoined 邏輯反轉即可實作「僅允許名單內 IP」。從簡易的 ip.txt 到功能完整的 block_ip 類別,開發者可依專案規模選擇適當方案。核心觀念在於:正確取得客戶端真實 IP + 靈活的規則表示法 + 高效能比對演算法。建議生產環境採用類別版並搭配快取機制,確保安全且不影響回應速度。