addHours($effectiveHours); // 存儲到 Redis Redis::setex( self::AUTH_TOKEN_PREFIX . $token, $effectiveSeconds, json_encode([ 'client_id' => $client->id, 'token' => $token, 'created_at' => now()->toIso8601String(), 'expires_at' => $expiresAt->toIso8601String(), ]) ); // 記錄操作日誌 $this->logService->logOperation( 'client', $client->id, 'Generated new auth token' ); return [ 'auth_token' => $token, 'expires_at' => $expiresAt->toIso8601String(), 'client_id' => $client->id, ]; } /** * 驗證認證令牌 * * @return array{client_id: int, token: string, created_at: string, expires_at: string}|null */ public function validateAuthToken(string $token): ?array { // 檢查黑名單 if (Redis::exists(self::BLACKLIST_PREFIX . $token)) { return null; } // 檢查令牌是否存在且有效 $tokenData = Redis::get(self::AUTH_TOKEN_PREFIX . $token); if (!$tokenData) { return null; } $data = json_decode($tokenData, true); if (!$data || now()->isAfter($data['expires_at'])) { Redis::del(self::AUTH_TOKEN_PREFIX . $token); return null; } return $data; } /** * 生成訪問令牌 * * @return array{access_token: string, expires_in: int} */ public function generateAccessToken(array $authTokenData): array { $accessToken = Str::random(64); // 存儲到 Redis,包含更多信息以便追蹤 Redis::setex( self::ACCESS_TOKEN_PREFIX . $accessToken, self::ACCESS_TOKEN_TTL, json_encode([ 'client_id' => $authTokenData['client_id'], 'auth_token' => $authTokenData['token'], 'created_at' => now()->toIso8601String(), ]) ); // 記錄操作日誌 $this->logService->logOperation( 'client', $authTokenData['client_id'], 'Generated new access token' ); return [ 'access_token' => $accessToken, 'expires_in' => self::ACCESS_TOKEN_TTL, ]; } /** * 驗證訪問令牌 * * @return array{client_id: int, auth_token: string, created_at: string}|null */ public function validateAccessToken(string $token): ?array { $tokenData = Redis::get(self::ACCESS_TOKEN_PREFIX . $token); if (!$tokenData) { return null; } return json_decode($tokenData, true); } /** * 撤銷訪問令牌 */ public function revokeAccessToken(string $token): void { $tokenData = Redis::get(self::ACCESS_TOKEN_PREFIX . $token); if ($tokenData) { $data = json_decode($tokenData, true); if ($data) { // 記錄操作日誌 $this->logService->logOperation( 'client', $data['client_id'], 'Access token revoked' ); } // 從 Redis 中刪除 Redis::del(self::ACCESS_TOKEN_PREFIX . $token); } } /** * 撤銷認證令牌 */ public function revokeAuthToken(string $token): void { $tokenData = Redis::get(self::AUTH_TOKEN_PREFIX . $token); if ($tokenData) { $data = json_decode($tokenData, true); if ($data) { // 計算剩餘有效期 $expiresAt = now()->parse($data['expires_at']); $remainingSeconds = max(1, $expiresAt->diffInSeconds(now())); // 加入黑名單 Redis::setex( self::BLACKLIST_PREFIX . $token, $remainingSeconds, 'revoked' ); // 記錄操作日誌 $this->logService->logOperation( 'client', $data['client_id'], 'Auth token revoked' ); } // 從 Redis 中刪除 Redis::del(self::AUTH_TOKEN_PREFIX . $token); } } }