diff --git a/Access_Core.php b/Access_Core.php index 86526d6..52f91de 100644 --- a/Access_Core.php +++ b/Access_Core.php @@ -92,6 +92,34 @@ class Access_Core return $entrypoint; } + /** + * 判断当前 IP 是否在屏蔽 IP(段) 中 + * + * @access private + * @return bool + */ + private function isBlockIp($ip): ?bool + { + $version = Access_Ip::matchIPVersion($ip); + if ($version === null) { + return false; + } + + $lines = explode('\n', $this->config->blockIps); + foreach ($lines as $line) { + $cidr = explode('#', $line)[0]; + $cidr = trim($cidr); + + if (!empty($cidr)) { + if (Access_Ip::matchCIDR($cidr, $ip)) { + return true; + } + } + } + + return false; + } + /** * 记录当前访问(管理员登录不会记录) * @@ -107,6 +135,9 @@ class Access_Core $url = $this->request->getServer('REQUEST_URI'); } $ip = $this->request->getIp(); + if ($this->isBlockIp($ip)) { + return; + } if(!empty($ip)) { # 解析ip归属地 try { diff --git a/Access_Ip.php b/Access_Ip.php new file mode 100644 index 0000000..5a22b0a --- /dev/null +++ b/Access_Ip.php @@ -0,0 +1,126 @@ += 2 ? $parts[1] : '32'; + return (ip2long($addr) >> (32 - $cidr_mask) == ip2long($cidr_ip) >> (32 - $cidr_mask)); + } + + /** + * Convert an ipv6 address to bin string + * @param string $addr - an ipv6 address + * @return string return the binary string of an ipv6 address if parameter ip6 is an ipv6 address, + * else it return an empty string. + */ + public static function ExpandIPv6Notation2Bin($addr) + { + if (strpos($addr, '::') !== false) { + $addr = str_replace('::', str_repeat(':0', 8 - substr_count($addr, ':')) . ':', $addr); + } + $ip6parts = explode(':', $addr); + $res = ""; + foreach ($ip6parts as $part) { + $res .= str_pad(base_convert($part, 16, 2), 16, 0, STR_PAD_LEFT); + } + return $res; + } + + /** + * Check if an ipv6 address is in the CIDRv6 subnet. + * @param string $cidr - an ipv6 subnet, ex 2001:288:5400/39 or 2001:288:5432:/64 or 2001:288:5478::/64.. + * @param string $addr - an ipv6 address, ex ::1, 2001:288:5200::1, :: ,etc. + * @return bool return true if $addr is inside the $cidr subnet, or return false. + */ + public static function MatchCIDRv6($cidr, $addr) + { + $parts = explode('/', $cidr); + $cidr_ip = $parts[0]; + $cidr_mask = count($parts) >= 2 ? $parts[1] : '128'; + $cidr_bin = substr(Access_Ip::ExpandIPv6Notation2Bin($cidr_ip), 0, $cidr_mask); + $ip_bin = substr(Access_Ip::ExpandIPv6Notation2Bin($addr), 0, $cidr_mask); + if (!strcmp($cidr_bin, $ip_bin)) + return true; + return false; + } +} diff --git a/Plugin.php b/Plugin.php index 1b42742..e4e75b3 100644 --- a/Plugin.php +++ b/Plugin.php @@ -75,9 +75,13 @@ class Access_Plugin implements Typecho_Plugin_Interface '0' => '后端', '1' => '前端', ), '0', '日志写入类型:', '请选择日志写入类型,如果写入速度较慢可选择前端写入日志。
如果您使用了pjax,请在pjax相关事件中调用 window.Access.track() 方法。'); + $blockIps = new Typecho_Widget_Helper_Form_Element_Textarea( + 'blockIps', null, '', + 'IP 黑名单', '每行一个,不记录来自这些 IP 的访问记录,支持 CIDR 掩码配置,支持 # 行注释'); $form->addInput($pageSize); $form->addInput($isDrop); $form->addInput($writeType); + $form->addInput($blockIps); } /**