openApiService = $openApiService; } /** * 处理请求 * * @param Request $request * @param Closure $next * @param string|null $limit 限制规则,格式:60,1 表示每分钟60次 * @return mixed */ public function handle(Request $request, Closure $next, string $limit = null) { // 获取应用信息 $app = $this->getAppFromRequest($request); if (!$app) { return $this->rateLimitResponse('应用信息不存在'); } // 解析限制规则 $rateLimit = $this->parseRateLimit($limit, $app); // 检查频率限制 if (!$this->checkRateLimit($app, $rateLimit, $request)) { return $this->rateLimitResponse('请求频率超出限制'); } // 记录请求 $this->recordRequest($app, $request); return $next($request); } /** * 从请求中获取应用信息 * * @param Request $request * @return OpenApiApp|null */ protected function getAppFromRequest(Request $request): ?OpenApiApp { // 从请求属性中获取应用信息(由认证中间件设置) return $request->attributes->get('openapi_app'); } /** * 解析限制规则 * * @param string|null $limit * @param OpenApiApp $app * @return array */ protected function parseRateLimit(?string $limit, OpenApiApp $app): array { // 如果没有指定限制,使用应用配置的限制 if (!$limit) { $rateLimits = $app->rate_limits ?? []; return [ 'requests_per_minute' => $rateLimits['requests_per_minute'] ?? 60, 'requests_per_hour' => $rateLimits['requests_per_hour'] ?? 1000, 'requests_per_day' => $rateLimits['requests_per_day'] ?? 10000, ]; } // 解析格式:60,1 表示每分钟60次 $parts = explode(',', $limit); $maxAttempts = (int)($parts[0] ?? 60); $decayMinutes = (int)($parts[1] ?? 1); return [ 'requests_per_minute' => $decayMinutes === 1 ? $maxAttempts : 0, 'requests_per_hour' => $decayMinutes === 60 ? $maxAttempts : 0, 'requests_per_day' => $decayMinutes === 1440 ? $maxAttempts : 0, ]; } /** * 检查频率限制 * * @param OpenApiApp $app * @param array $rateLimit * @param Request $request * @return bool */ protected function checkRateLimit(OpenApiApp $app, array $rateLimit, Request $request): bool { $ip = $request->ip(); $appId = $app->app_id; // 检查每分钟限制 if ($rateLimit['requests_per_minute'] > 0) { $key = "rate_limit:minute:{$appId}:{$ip}"; if (!$this->checkLimit($key, $rateLimit['requests_per_minute'], 60)) { return false; } } // 检查每小时限制 if ($rateLimit['requests_per_hour'] > 0) { $key = "rate_limit:hour:{$appId}:{$ip}"; if (!$this->checkLimit($key, $rateLimit['requests_per_hour'], 3600)) { return false; } } // 检查每天限制 if ($rateLimit['requests_per_day'] > 0) { $key = "rate_limit:day:{$appId}:{$ip}"; if (!$this->checkLimit($key, $rateLimit['requests_per_day'], 86400)) { return false; } } return true; } /** * 检查单个限制 * * @param string $key * @param int $maxAttempts * @param int $decaySeconds * @return bool */ protected function checkLimit(string $key, int $maxAttempts, int $decaySeconds): bool { $attempts = Cache::get($key, 0); if ($attempts >= $maxAttempts) { Log::warning("Rate limit exceeded", [ 'key' => $key, 'attempts' => $attempts, 'max_attempts' => $maxAttempts ]); return false; } // 增加计数 Cache::put($key, $attempts + 1, $decaySeconds); return true; } /** * 记录请求 * * @param OpenApiApp $app * @param Request $request * @return void */ protected function recordRequest(OpenApiApp $app, Request $request): void { // 这里可以记录请求日志,用于统计分析 Log::info("API request", [ 'app_id' => $app->app_id, 'ip' => $request->ip(), 'uri' => $request->getRequestUri(), 'method' => $request->getMethod(), 'user_agent' => $request->userAgent(), ]); } /** * 返回限流响应 * * @param string $message * @return \Illuminate\Http\JsonResponse */ protected function rateLimitResponse(string $message) { return response()->json([ 'error' => 'rate_limit_exceeded', 'message' => $message, 'retry_after' => 60, // 建议重试时间(秒) ], 429); } }