PHP 封禁状态检查中间件

wen PHP项目 4

本文目录导读:

PHP 封禁状态检查中间件

  1. 基础中间件实现
  2. 封禁服务类
  3. 用户模型添加封禁关系
  4. 封禁记录模型
  5. 路由注册中间件
  6. 使用示例
  7. 封禁页面视图(Blade)
  8. 配置封禁选项

我来为你提供一个完整的 PHP 封禁状态检查中间件实现:

基础中间件实现

<?php
namespace App\Middleware;
use Closure;
use App\Models\User;
use App\Services\BanService;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class CheckBanStatusMiddleware
{
    protected $banService;
    public function __construct(BanService $banService)
    {
        $this->banService = $banService;
    }
    public function handle(Request $request, Closure $next, string $type = 'user'): Response
    {
        // 获取当前认证用户
        $user = $request->user();
        if (!$user) {
            return $next($request);
        }
        // 检查用户是否被封禁
        if ($this->isUserBanned($user, $type)) {
            return $this->handleBannedUser($request, $user);
        }
        // 检查IP是否被封禁
        if ($this->isIpBanned($request->ip())) {
            return $this->handleBannedIp($request);
        }
        return $next($request);
    }
    protected function isUserBanned($user, string $type): bool
    {
        // 检查用户是否有封禁记录
        $ban = $user->bans()
            ->where('type', $type)
            ->where(function ($query) {
                $query->whereNull('expires_at')
                      ->orWhere('expires_at', '>', now());
            })
            ->latest()
            ->first();
        if (!$ban) {
            return false;
        }
        // 检查是否永久封禁或临时封禁
        if ($ban->is_permanent || $ban->expires_at > now()) {
            return true;
        }
        return false;
    }
    protected function isIpBanned(string $ip): bool
    {
        return $this->banService->isIpBanned($ip);
    }
    protected function handleBannedUser($request, $user): Response
    {
        if ($request->expectsJson()) {
            return response()->json([
                'success' => false,
                'message' => '您的账号已被封禁',
                'data' => [
                    'ban_reason' => $user->currentBan?->reason,
                    'ban_until' => $user->currentBan?->expires_at,
                    'is_permanent' => $user->currentBan?->is_permanent ?? false
                ]
            ], 403);
        }
        // 注销用户会话
        auth()->logout();
        $request->session()->invalidate();
        $request->session()->regenerateToken();
        return redirect()
            ->route('banned')
            ->with('error', '您的账号已被封禁');
    }
    protected function handleBannedIp($request): Response
    {
        if ($request->expectsJson()) {
            return response()->json([
                'success' => false,
                'message' => '当前IP已被封禁'
            ], 403);
        }
        abort(403, '当前IP已被封禁');
    }
}

封禁服务类

<?php
namespace App\Services;
use App\Models\User;
use App\Models\BanRecord;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Redis;
class BanService
{
    /**
     * 检查用户是否被封禁
     */
    public function checkUserBan(User $user): ?BanRecord
    {
        return $user->bans()
            ->where(function ($query) {
                $query->whereNull('expires_at')
                      ->orWhere('expires_at', '>', now());
            })
            ->where('is_active', true)
            ->latest()
            ->first();
    }
    /**
     * 检查IP是否被封禁
     */
    public function isIpBanned(string $ip): bool
    {
        // 使用缓存减少数据库查询
        return Cache::remember("banned_ip:{$ip}", 3600, function () use ($ip) {
            return BanRecord::where('ip_address', $ip)
                ->where('type', 'ip')
                ->where(function ($query) {
                    $query->whereNull('expires_at')
                          ->orWhere('expires_at', '>', now());
                })
                ->where('is_active', true)
                ->exists();
        });
    }
    /**
     * 封禁用户
     */
    public function banUser(User $user, array $data): BanRecord
    {
        $ban = $user->bans()->create([
            'type' => 'user',
            'reason' => $data['reason'],
            'expires_at' => $data['duration'] === 'permanent' ? null 
                          : now()->addDays($data['duration']),
            'is_permanent' => $data['duration'] === 'permanent',
            'banned_by' => auth()->id(),
            'ip_address' => request()->ip()
        ]);
        // 清除相关缓存
        $this->clearUserBanCache($user->id);
        $this->clearIpBanCache(request()->ip());
        return $ban;
    }
    /**
     * 解除封禁
     */
    public function unbanUser(int $userId): bool
    {
        $updated = BanRecord::where('user_id', $userId)
            ->where('is_active', true)
            ->update(['is_active' => false]);
        $this->clearUserBanCache($userId);
        return $updated > 0;
    }
    /**
     * 封禁IP
     */
    public function banIp(string $ip, array $data): BanRecord
    {
        $ban = BanRecord::create([
            'type' => 'ip',
            'ip_address' => $ip,
            'reason' => $data['reason'] ?? 'IP封禁',
            'expires_at' => $data['duration'] === 'permanent' ? null 
                          : now()->addDays($data['duration']),
            'is_permanent' => $data['duration'] === 'permanent',
            'banned_by' => auth()->id()
        ]);
        $this->clearIpBanCache($ip);
        return $ban;
    }
    /**
     * 检查登录尝试次数
     */
    public function checkLoginAttempts(string $email, string $ip): array
    {
        $key = "login_attempts:{$email}:{$ip}";
        $attempts = Redis::get($key) ?: 0;
        $maxAttempts = 5;
        return [
            'attempts' => (int)$attempts,
            'remaining' => max(0, $maxAttempts - (int)$attempts),
            'is_locked' => (int)$attempts >= $maxAttempts
        ];
    }
    /**
     * 记录登录失败
     */
    public function recordLoginFailure(string $email, string $ip): void
    {
        $key = "login_attempts:{$email}:{$ip}";
        $attempts = Redis::get($key) ?: 0;
        Redis::setex($key, 3600, (int)$attempts + 1);
    }
    /**
     * 清除登录失败记录
     */
    public function clearLoginAttempts(string $email, string $ip): void
    {
        Redis::del("login_attempts:{$email}:{$ip}");
    }
    protected function clearUserBanCache(int $userId): void
    {
        Cache::forget("user_ban:{$userId}");
        Cache::forget('user_ban_status:'.$userId);
    }
    protected function clearIpBanCache(string $ip): void
    {
        Cache::forget("banned_ip:{$ip}");
    }
}

用户模型添加封禁关系

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class User extends Model
{
    public function bans(): HasMany
    {
        return $this->hasMany(BanRecord::class);
    }
    public function currentBan(): ?BanRecord
    {
        return $this->bans()
            ->where(function ($query) {
                $query->whereNull('expires_at')
                      ->orWhere('expires_at', '>', now());
            })
            ->where('is_active', true)
            ->latest()
            ->first();
    }
    public function isBanned(): bool
    {
        return $this->currentBan() !== null;
    }
}

封禁记录模型

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class BanRecord extends Model
{
    protected $fillable = [
        'user_id',
        'type',
        'reason',
        'expires_at',
        'is_permanent',
        'is_active',
        'ip_address',
        'banned_by',
        'created_at',
        'updated_at'
    ];
    protected $casts = [
        'expires_at' => 'datetime',
        'is_permanent' => 'boolean',
        'is_active' => 'boolean'
    ];
    protected $dates = ['expires_at'];
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }
    public function bannedBy(): BelongsTo
    {
        return $this->belongsTo(User::class, 'banned_by');
    }
}

路由注册中间件

<?php
// routes/web.php 或 routes/api.php
use App\Middleware\CheckBanStatusMiddleware;
// Web 路由
Route::middleware(['auth', CheckBanStatusMiddleware::class])->group(function () {
    Route::get('/dashboard', 'DashboardController@index');
    // 其他需要检查封禁状态的路由
});
// API 路由
Route::middleware(['auth:sanctum', CheckBanStatusMiddleware::class])->group(function () {
    Route::get('/api/user/profile', 'Api\UserController@profile');
    // 其他API路由
});
// 自定义封禁类型
Route::middleware(['auth', 'ban.check:shop'])->group(function () {
    // 商店相关路由
});

使用示例

<?php
// 在控制器中使用
namespace App\Http\Controllers;
use App\Services\BanService;
class AdminController extends Controller
{
    protected $banService;
    public function __construct(BanService $banService)
    {
        $this->banService = $banService;
    }
    public function banUser(Request $request, $userId)
    {
        $user = User::findOrFail($userId);
        $ban = $this->banService->banUser($user, [
            'reason' => $request->reason,
            'duration' => $request->duration // 'permanent' 或天数
        ]);
        return response()->json([
            'success' => true,
            'message' => '用户已被封禁',
            'data' => $ban
        ]);
    }
    public function unbanUser($userId)
    {
        $this->banService->unbanUser($userId);
        return response()->json([
            'success' => true,
            'message' => '用户封禁已解除'
        ]);
    }
}

封禁页面视图(Blade)

{{-- resources/views/banned.blade.php --}}
@extends('layouts.app')
@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header bg-danger text-white">
                    <h3><i class="fas fa-ban"></i> 账号封禁通知</h3>
                </div>
                <div class="card-body">
                    @if(session('error'))
                        <div class="alert alert-danger">
                            {{ session('error') }}
                        </div>
                    @endif
                    <p>您的账号已被封禁,如果这是误判,请联系管理员申诉。</p>
                    @if(isset($banReason))
                        <p><strong>封禁原因:</strong>{{ $banReason }}</p>
                    @endif
                    @if(isset($banUntil))
                        <p><strong>封禁至:</strong>{{ $banUntil->format('Y-m-d H:i:s') }}</p>
                    @endif
                    <a href="{{ route('home') }}" class="btn btn-primary">返回首页</a>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

配置封禁选项

<?php
// config/ban.php
return [
    'types' => [
        'user' => '用户封禁',
        'ip' => 'IP封禁',
        'shop' => '店铺封禁',
    ],
    'durations' => [
        '1' => '1天',
        '3' => '3天',
        '7' => '7天',
        '30' => '30天',
        'permanent' => '永久',
    ],
    'max_login_attempts' => 5,
    'login_lockout_minutes' => 60,
];

这个中间件系统包含了:

  1. 完整的封禁检查逻辑
  2. 用户和IP双重检查
  3. JSON和Web响应支持
  4. 缓存优化性能
  5. 灵活的封禁类型
  6. 封禁和解封管理
  7. 登录限制功能

你可以根据实际需求调整封禁逻辑和响应方式。

抱歉,评论功能暂时关闭!