本文目录导读:

在PHP项目中,要统计“关门防守成功”的次数,通常指的是阻止特定攻击(如SQL注入、XSS、暴力破解等)或限制非法访问的次数,具体实现取决于你的“防守”逻辑是什么。
以下是几种常见场景的统计实现方案:
基于中间件/拦截器的统计(推荐)
如果你使用 Laravel、Symfony 或 ThinkPHP,最好的方式是在全局中间件中统计。
创建日志/统计表
CREATE TABLE defense_logs (
id INT AUTO_INCREMENT PRIMARY KEY,
type VARCHAR(50) NOT NULL COMMENT '防守类型: sql_injection, brute_force, rate_limit',
ip_address VARCHAR(45) NOT NULL,
count INT DEFAULT 0,
last_attempt_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_type_ip (type, ip_address)
);
中间件逻辑(Laravel示例)
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\DB;
class SecurityMiddleware
{
public function handle($request, Closure $next)
{
// --- 防守检测逻辑 ---
$blocked = false;
$type = 'unknown';
// 示例1:检测SQL注入(简单正则)
if (preg_match('/\b(SELECT|UNION|INSERT|DELETE)\b/i', $request->getQueryString() ?? '')) {
$blocked = true;
$type = 'sql_injection';
}
// 示例2:频率限制(防暴力破解)
$ip = $request->ip();
$key = 'login_attempts_' . $ip;
if (cache($key, 0) >= 5) {
$blocked = true;
$type = 'brute_force';
}
// --- 如果触发了防守,则记录 ---
if ($blocked) {
// 原子更新统计(防止并发冲突)
DB::table('defense_logs')
->updateOrInsert(
['type' => $type, 'ip_address' => $ip],
['last_attempt_at' => now()]
);
// 或者使用 Redis 计数器
Redis::incr("defense:{$type}:{$ip}");
// 返回阻断响应
return response('Forbidden', 403);
}
return $next($request);
}
}
基于业务逻辑的统计(登录/API调用)
如果你的“关门”特指限制某用户登录失败或API请求超限:
<?php
class LoginService
{
private $redis;
private $maxAttempts = 5; // 最多尝试次数
public function __construct(\Redis $redis)
{
$this->redis = $redis;
}
public function attemptLogin($username, $password)
{
$key = "login_lock:{$username}";
// 1. 检查是否已被锁住
if ($this->redis->get($key) >= $this->maxAttempts) {
// 统计防守成功次数(自增)
$this->redis->incr("defense_count:login_lock");
return ['success' => false, 'msg' => '账户已被锁定,请稍后再试'];
}
// 2. 模拟验证登陆
if ($this->validateUser($username, $password)) {
$this->redis->del($key);
return ['success' => true];
} else {
// 登录失败,增加尝试次数
$attempts = $this->redis->incr($key);
// 如果达到阈值,记录防守成功
if ($attempts == $this->maxAttempts) {
$this->redis->incr("defense_count:login_lock");
}
return ['success' => false, 'msg' => '账号或密码错误'];
}
}
public function getTotalDefenseCount()
{
return (int)$this->redis->get("defense_count:login_lock") ?: 0;
}
}
通用文件/数据库日志统计(简单项目)
如果不依赖框架,可以用文件记录所有拦截日志,然后用PHP脚本汇总。
写入日志:
<?php
function logDefenseEvent($type, $ip) {
$logFile = __DIR__ . '/defense.log'; // 格式: '2024-01-01 12:00:00|sql_injection|192.168.1.1'
$line = date('Y-m-d H:i:s') . "|{$type}|{$ip}\n";
file_put_contents($logFile, $line, FILE_APPEND);
}
// 在防守触发处调用
if (isset($_GET['id']) && preg_match('/^[0-9]+$/', $_GET['id']) === 0) {
logDefenseEvent('sql_injection', $_SERVER['REMOTE_ADDR']);
// 拦截处理...
die('非法参数');
}
统计输出:
<?php
function getDefenseStats() {
$stats = [];
$lines = file(__DIR__ . '/defense.log', FILE_IGNORE_NEW_LINES);
foreach ($lines as $line) {
$parts = explode('|', $line);
if (count($parts) === 3) {
$type = $parts[1];
$stats[$type] = ($stats[$type] ?? 0) + 1;
}
}
return $stats;
}
// 查看统计
print_r(getDefenseStats());
// 输出: ['sql_injection' => 10, 'brute_force' => 3]
实时仪表板(使用Redis统计)
适合需要实时展示的场景:
<?php
// 防守成功时(Redis计数器)
$redis->incr("defense_total"); // 总次数
$redis->incr("defense_today:" . date('Y-m-d')); // 今日次数
$redis->incr("defense_type:{$type}"); // 按类型统计
// 查询统计
$total = $redis->get("defense_total") ?: 0;
$today = $redis->get("defense_today:" . date('Y-m-d')) ?: 0;
$sqlInjection = $redis->get("defense_type:sql_injection") ?: 0;
重要提示
- 并发问题:使用数据库或Redis的
INCR原子操作,避免SELECT→UPDATE造成的计数丢失。 - 不记录攻击者:如果想记录攻击者IP,可以增加字段。
- 区分类型:建议统计时区分
sql注入、XSS、暴力破解等不同防御类型。
如果你能告诉我具体是哪种“防守”(比如是防火墙、验证码、防刷还是登录限制),我可以给出更精确的代码。