header('X-STOREFRONT-KEY'); if ($storefrontKey) { $apiKeyService = $this->getApiKeyService(); // 验证 key 有效性 $validation = $apiKeyService->validate( $storefrontKey, ApiKeyService::KEY_TYPE_SHOP, $request->ip() ); if (! ($validation['valid'] ?? false)) { return response()->json([ 'error' => 'invalid_storefront_key', 'message' => $validation['message'] ?? 'The provided storefront key is invalid or inactive.', ], 403); } // 频率限制检查 $rateLimit = $apiKeyService->checkRateLimit($validation['client']); if (! ($rateLimit['allowed'] ?? false)) { return response()->json([ 'error' => 'rate_limit_exceeded', 'message' => 'Rate limit exceeded. Please retry later.', 'retry_after' => $rateLimit['reset_at'] ?? 60, ], 429); } return $next($request); } else { return response()->json([ 'error' => 'invalid_storefront_key', 'message' => 'X-STOREFRONT-KEY header is required', ], 403); } // 方式 1: Session Cookie 认证(传统 Web 登录) if (auth()->guard('customer')->check()) { return $next($request); } // 方式 2: Sanctum Bearer Token 认证(BagistoApi 登录) if ($request->bearerToken()) { $customer = $this->authenticateViaSanctum($request); if ($customer) { // 将 Sanctum 认证的用户设置到 customer guard auth()->guard('customer')->setUser($customer); return $next($request); } } // 都未认证,返回 JSON 错误 return response()->json([ 'error' => 'Please login first', 'message' => 'Authentication required. Please login via /customer/login (Session) or /api/shop/customer/login (Token)' ], 401); } /** * 懒加载 ApiKeyService 实例 */ protected function getApiKeyService(): ApiKeyService { return $this->apiKeyService ??= app(ApiKeyService::class); } /** * 通过 Sanctum Token 验证客户 * * @return \Webkul\Customer\Models\Customer|null */ protected function authenticateViaSanctum(Request $request) { $token = $request->bearerToken(); if (!$token) { return null; } try { // 使用 Sanctum 的 PersonalAccessToken 模型验证 $accessToken = PersonalAccessToken::findToken($token); if (!$accessToken) { return null; } // 检查 Token 是否过期 if ($accessToken->expires_at && $accessToken->expires_at->isPast()) { return null; } // 获取关联的用户模型 $tokenable = $accessToken->tokenable; // 确保是 Customer 模型 if (!$tokenable || !($tokenable instanceof \Webkul\Customer\Models\Customer)) { return null; } // 更新 Token 最后使用时间 $accessToken->touch(); return $tokenable; } catch (\Exception $e) { // 记录错误日志(可选) \Log::debug('Sanctum authentication failed', [ 'error' => $e->getMessage() ]); return null; } } }