token system change
This commit is contained in:
parent
ae5a7662ca
commit
33dde9dc2c
6 changed files with 189 additions and 192 deletions
|
|
@ -1,35 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use App\Models\AuthToken;
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
|
|
||||||
class CleanupExpiredTokens extends Command
|
|
||||||
{
|
|
||||||
protected $signature = 'auth:cleanup-expired-tokens';
|
|
||||||
protected $description = 'Clean up expired auth tokens from the database';
|
|
||||||
|
|
||||||
public function handle(): void
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$count = AuthToken::where('expires_at', '<', now())->delete();
|
|
||||||
|
|
||||||
if ($count > 0) {
|
|
||||||
Log::info("Cleaned up {$count} expired auth tokens");
|
|
||||||
$this->info("Successfully cleaned up {$count} expired auth tokens.");
|
|
||||||
} else {
|
|
||||||
$this->info('No expired auth tokens found.');
|
|
||||||
}
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('Error cleaning up expired auth tokens', [
|
|
||||||
'error' => $e->getMessage(),
|
|
||||||
'trace' => $e->getTraceAsString(),
|
|
||||||
]);
|
|
||||||
$this->error('Failed to clean up expired auth tokens.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -12,8 +12,7 @@ class Kernel extends ConsoleKernel
|
||||||
*/
|
*/
|
||||||
protected function schedule(Schedule $schedule): void
|
protected function schedule(Schedule $schedule): void
|
||||||
{
|
{
|
||||||
// Clean up expired auth tokens daily
|
// 移除不必要的清理任務
|
||||||
$schedule->command('auth:cleanup-expired-tokens')->daily();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -6,92 +6,68 @@
|
||||||
|
|
||||||
class ErrorCode
|
class ErrorCode
|
||||||
{
|
{
|
||||||
// 通用错误 (1000-1999)
|
// Authentication & Authorization
|
||||||
public const VALIDATION_ERROR = 'E1001';
|
public const VALIDATION_ERROR = 'validation_error';
|
||||||
public const UNAUTHORIZED = 'E1002';
|
public const SERVER_ERROR = 'server_error';
|
||||||
public const FORBIDDEN = 'E1003';
|
public const NOT_FOUND = 'not_found';
|
||||||
public const RESOURCE_NOT_FOUND = 'E1004';
|
public const UNAUTHORIZED = 'unauthorized';
|
||||||
public const METHOD_NOT_ALLOWED = 'E1005';
|
public const FORBIDDEN = 'forbidden';
|
||||||
public const TOO_MANY_REQUESTS = 'E1006';
|
public const TOKEN_INVALID = 'token_invalid';
|
||||||
public const SERVER_ERROR = 'E1007';
|
public const TOKEN_EXPIRED = 'token_expired';
|
||||||
public const SERVICE_UNAVAILABLE = 'E1008';
|
public const INVALID_CREDENTIALS = 'invalid_credentials';
|
||||||
|
|
||||||
// 认证相关错误 (2000-2999)
|
// Resource Related
|
||||||
public const INVALID_CREDENTIALS = 'E2001';
|
public const RESOURCE_NOT_FOUND = 'resource_not_found';
|
||||||
public const TOKEN_EXPIRED = 'E2002';
|
public const RESOURCE_IN_USE = 'resource_in_use';
|
||||||
public const TOKEN_INVALID = 'E2003';
|
|
||||||
public const TOKEN_BLACKLISTED = 'E2004';
|
|
||||||
public const TOKEN_NOT_FOUND = 'E2005';
|
|
||||||
|
|
||||||
// 客户相关错误 (3000-3999)
|
// Request Related
|
||||||
public const CLIENT_NOT_FOUND = 'E3001';
|
public const INVALID_REQUEST_FORMAT = 'invalid_request_format';
|
||||||
public const CLIENT_INACTIVE = 'E3002';
|
public const METHOD_NOT_ALLOWED = 'method_not_allowed';
|
||||||
public const CLIENT_RATE_LIMIT_EXCEEDED = 'E3003';
|
public const TOO_MANY_REQUESTS = 'too_many_requests';
|
||||||
public const CLIENT_QUOTA_EXCEEDED = 'E3004';
|
|
||||||
public const CLIENT_HAS_ACTIVE_TOKENS = 'E3005';
|
|
||||||
|
|
||||||
// LLM 提供商相关错误 (4000-4999)
|
// Client Related
|
||||||
public const PROVIDER_NOT_FOUND = 'E4001';
|
public const CLIENT_INACTIVE = 'client_inactive';
|
||||||
public const PROVIDER_INACTIVE = 'E4002';
|
public const CLIENT_NOT_FOUND = 'client_not_found';
|
||||||
public const PROVIDER_ERROR = 'E4003';
|
public const CLIENT_HAS_ACTIVE_TOKENS = 'client_has_active_tokens';
|
||||||
public const PROVIDER_TIMEOUT = 'E4004';
|
|
||||||
public const PROVIDER_RATE_LIMIT = 'E4005';
|
|
||||||
|
|
||||||
// 业务逻辑错误 (5000-5999)
|
// Provider Related
|
||||||
public const INVALID_REQUEST_FORMAT = 'E5001';
|
public const PROVIDER_ERROR = 'provider_error';
|
||||||
public const INVALID_RESPONSE_FORMAT = 'E5002';
|
public const LLM_PROVIDER_NOT_FOUND = 'llm_provider_not_found';
|
||||||
public const OPERATION_FAILED = 'E5003';
|
public const LLM_REQUEST_FAILED = 'llm_request_failed';
|
||||||
public const RESOURCE_ALREADY_EXISTS = 'E5004';
|
|
||||||
public const RESOURCE_IN_USE = 'E5005';
|
private static array $messages = [
|
||||||
|
// Authentication & Authorization
|
||||||
|
self::VALIDATION_ERROR => '請求參數驗證失敗。',
|
||||||
|
self::SERVER_ERROR => '服務器內部錯誤。',
|
||||||
|
self::NOT_FOUND => '請求的資源不存在。',
|
||||||
|
self::UNAUTHORIZED => '未授權,請先登錄。',
|
||||||
|
self::FORBIDDEN => '禁止訪問,權限不足。',
|
||||||
|
self::TOKEN_INVALID => '令牌無效。',
|
||||||
|
self::TOKEN_EXPIRED => '令牌已過期。',
|
||||||
|
self::INVALID_CREDENTIALS => '認證憑據無效。',
|
||||||
|
|
||||||
|
// Resource Related
|
||||||
|
self::RESOURCE_NOT_FOUND => '請求的資源不存在。',
|
||||||
|
self::RESOURCE_IN_USE => '資源正在使用中,無法執行操作。',
|
||||||
|
|
||||||
|
// Request Related
|
||||||
|
self::INVALID_REQUEST_FORMAT => '請求格式無效。',
|
||||||
|
self::METHOD_NOT_ALLOWED => '請求方法不允許。',
|
||||||
|
self::TOO_MANY_REQUESTS => '請求過於頻繁,請稍後再試。',
|
||||||
|
|
||||||
|
// Client Related
|
||||||
|
self::CLIENT_INACTIVE => '客戶帳戶未啟用。',
|
||||||
|
self::CLIENT_NOT_FOUND => '客戶不存在。',
|
||||||
|
self::CLIENT_HAS_ACTIVE_TOKENS => '客戶有活躍的認證令牌。',
|
||||||
|
|
||||||
|
// Provider Related
|
||||||
|
self::PROVIDER_ERROR => '提供商服務錯誤。',
|
||||||
|
self::LLM_PROVIDER_NOT_FOUND => 'LLM 提供商不存在。',
|
||||||
|
self::LLM_REQUEST_FAILED => 'LLM 請求失敗。',
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取错误代码对应的默认消息
|
|
||||||
*
|
|
||||||
* @param string $code 错误代码
|
|
||||||
* @return string 错误消息
|
|
||||||
*/
|
|
||||||
public static function getMessage(string $code): string
|
public static function getMessage(string $code): string
|
||||||
{
|
{
|
||||||
return match ($code) {
|
return self::$messages[$code] ?? '未知錯誤。';
|
||||||
// 通用错误
|
|
||||||
self::VALIDATION_ERROR => '请求参数验证失败。',
|
|
||||||
self::UNAUTHORIZED => '未经授权的访问。',
|
|
||||||
self::FORBIDDEN => '无权访问该资源。',
|
|
||||||
self::RESOURCE_NOT_FOUND => '请求的资源不存在。',
|
|
||||||
self::METHOD_NOT_ALLOWED => '请求方法不允许。',
|
|
||||||
self::TOO_MANY_REQUESTS => '请求过于频繁,请稍后重试。',
|
|
||||||
self::SERVER_ERROR => '服务器内部错误。',
|
|
||||||
self::SERVICE_UNAVAILABLE => '服务暂时不可用。',
|
|
||||||
|
|
||||||
// 认证相关错误
|
|
||||||
self::INVALID_CREDENTIALS => '无效的认证凭据。',
|
|
||||||
self::TOKEN_EXPIRED => '认证令牌已过期。',
|
|
||||||
self::TOKEN_INVALID => '无效的认证令牌。',
|
|
||||||
self::TOKEN_BLACKLISTED => '认证令牌已被禁用。',
|
|
||||||
self::TOKEN_NOT_FOUND => '认证令牌不存在。',
|
|
||||||
|
|
||||||
// 客户相关错误
|
|
||||||
self::CLIENT_NOT_FOUND => '客户不存在。',
|
|
||||||
self::CLIENT_INACTIVE => '客户状态为非活跃。',
|
|
||||||
self::CLIENT_RATE_LIMIT_EXCEEDED => '已超过请求速率限制。',
|
|
||||||
self::CLIENT_QUOTA_EXCEEDED => '已超过配额限制。',
|
|
||||||
self::CLIENT_HAS_ACTIVE_TOKENS => '客户存在活跃的认证令牌。',
|
|
||||||
|
|
||||||
// LLM 提供商相关错误
|
|
||||||
self::PROVIDER_NOT_FOUND => 'LLM 提供商不存在。',
|
|
||||||
self::PROVIDER_INACTIVE => 'LLM 提供商状态为非活跃。',
|
|
||||||
self::PROVIDER_ERROR => 'LLM 提供商服务错误。',
|
|
||||||
self::PROVIDER_TIMEOUT => 'LLM 提供商服务超时。',
|
|
||||||
self::PROVIDER_RATE_LIMIT => 'LLM 提供商限制请求速率。',
|
|
||||||
|
|
||||||
// 业务逻辑错误
|
|
||||||
self::INVALID_REQUEST_FORMAT => '无效的请求格式。',
|
|
||||||
self::INVALID_RESPONSE_FORMAT => '无效的响应格式。',
|
|
||||||
self::OPERATION_FAILED => '操作失败。',
|
|
||||||
self::RESOURCE_ALREADY_EXISTS => '资源已存在。',
|
|
||||||
self::RESOURCE_IN_USE => '资源正在使用中。',
|
|
||||||
|
|
||||||
default => '未知错误。',
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,7 @@ public function __construct(
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取访问令牌
|
* 獲取訪問令牌
|
||||||
*
|
|
||||||
* @param Request $request
|
|
||||||
* @return JsonResponse
|
|
||||||
*/
|
*/
|
||||||
public function getAccessToken(Request $request): JsonResponse
|
public function getAccessToken(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
|
|
@ -34,11 +31,28 @@ public function getAccessToken(Request $request): JsonResponse
|
||||||
'auth_token' => 'required|string|size:64',
|
'auth_token' => 'required|string|size:64',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$result = $this->tokenService->generateAccessToken($validated['auth_token']);
|
$authTokenData = $this->tokenService->validateAuthToken($validated['auth_token']);
|
||||||
|
|
||||||
|
if (!$authTokenData) {
|
||||||
|
return $this->error(
|
||||||
|
ErrorCode::TOKEN_INVALID,
|
||||||
|
'認證令牌無效。'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (now()->isAfter($authTokenData['expires_at'])) {
|
||||||
|
return $this->error(
|
||||||
|
ErrorCode::TOKEN_EXPIRED,
|
||||||
|
'認證令牌已過期,請重新獲取。'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $this->tokenService->generateAccessToken($authTokenData);
|
||||||
|
|
||||||
return $this->success([
|
return $this->success([
|
||||||
'access_token' => $result['token'],
|
'access_token' => $result['access_token'],
|
||||||
'expires_at' => $result['expires_at'],
|
'expires_in' => $result['expires_in'],
|
||||||
|
'token_type' => $result['token_type'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
} catch (ValidationException $e) {
|
} catch (ValidationException $e) {
|
||||||
|
|
@ -49,7 +63,7 @@ public function getAccessToken(Request $request): JsonResponse
|
||||||
);
|
);
|
||||||
} catch (\InvalidArgumentException $e) {
|
} catch (\InvalidArgumentException $e) {
|
||||||
return $this->error(
|
return $this->error(
|
||||||
ErrorCode::TOKEN_INVALID,
|
ErrorCode::TOKEN_EXPIRED,
|
||||||
$e->getMessage()
|
$e->getMessage()
|
||||||
);
|
);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,11 @@ public function client(): BelongsTo
|
||||||
|
|
||||||
public function isValid(): bool
|
public function isValid(): bool
|
||||||
{
|
{
|
||||||
if ($this->expires_at === null) {
|
return $this->expires_at && $this->expires_at->isFuture();
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return $this->expires_at->isFuture();
|
public function isExpired(): bool
|
||||||
|
{
|
||||||
|
return !$this->isValid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,98 +12,111 @@
|
||||||
|
|
||||||
class TokenService
|
class TokenService
|
||||||
{
|
{
|
||||||
|
private const AUTH_TOKEN_PREFIX = 'auth_token:';
|
||||||
private const ACCESS_TOKEN_PREFIX = 'access_token:';
|
private const ACCESS_TOKEN_PREFIX = 'access_token:';
|
||||||
private const ACCESS_TOKEN_TTL = 3600; // 1 hour in seconds
|
private const BLACKLIST_PREFIX = 'token_blacklist:';
|
||||||
|
private const MAX_AUTH_TOKEN_HOURS = 8; // 最大 8 小時有效期
|
||||||
|
private const ACCESS_TOKEN_TTL = 3600; // 1 小時有效期
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly LogService $logService
|
private readonly LogService $logService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function generateAuthToken(Client $client, ?int $expiresInDays = null): AuthToken
|
public function generateAuthToken(Client $client, ?int $expiresInHours = null): AuthToken
|
||||||
{
|
{
|
||||||
$token = AuthToken::create([
|
// 確保不超過最大有效期
|
||||||
'client_id' => $client->id,
|
$effectiveHours = min($expiresInHours ?? self::MAX_AUTH_TOKEN_HOURS, self::MAX_AUTH_TOKEN_HOURS);
|
||||||
'token' => Str::random(64),
|
$effectiveSeconds = $effectiveHours * 3600;
|
||||||
'expires_at' => $expiresInDays ? now()->addDays($expiresInDays) : null,
|
|
||||||
]);
|
|
||||||
|
|
||||||
|
// 生成新令牌
|
||||||
|
$token = Str::random(64);
|
||||||
|
$expiresAt = now()->addHours($effectiveHours);
|
||||||
|
|
||||||
|
// 存儲到 Redis
|
||||||
|
Redis::setex(
|
||||||
|
self::AUTH_TOKEN_PREFIX . $token,
|
||||||
|
$effectiveSeconds,
|
||||||
|
json_encode([
|
||||||
|
'client_id' => $client->id,
|
||||||
|
'created_at' => now()->toIso8601String(),
|
||||||
|
'expires_at' => $expiresAt->toIso8601String(),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
// 記錄操作日誌
|
||||||
$this->logService->logOperation(
|
$this->logService->logOperation(
|
||||||
'client',
|
'client',
|
||||||
$client->id,
|
$client->id,
|
||||||
'Generated new auth token'
|
'Generated new auth token'
|
||||||
);
|
);
|
||||||
|
|
||||||
return $token;
|
// 返回令牌對象(僅用於 API 響應)
|
||||||
|
return new AuthToken([
|
||||||
|
'client_id' => $client->id,
|
||||||
|
'token' => $token,
|
||||||
|
'expires_at' => $expiresAt,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function generateAccessToken(AuthToken $authToken): array
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateAccessToken(array $authTokenData): array
|
||||||
{
|
{
|
||||||
$accessToken = Str::random(64);
|
$accessToken = Str::random(64);
|
||||||
$expiresAt = now()->addSeconds(self::ACCESS_TOKEN_TTL);
|
|
||||||
|
|
||||||
$tokenData = [
|
|
||||||
'client_id' => $authToken->client_id,
|
|
||||||
'expires_at' => $expiresAt->toIso8601String(),
|
|
||||||
];
|
|
||||||
|
|
||||||
|
// 存儲到 Redis,包含更多信息以便追蹤
|
||||||
Redis::setex(
|
Redis::setex(
|
||||||
self::ACCESS_TOKEN_PREFIX . $accessToken,
|
self::ACCESS_TOKEN_PREFIX . $accessToken,
|
||||||
self::ACCESS_TOKEN_TTL,
|
self::ACCESS_TOKEN_TTL,
|
||||||
json_encode($tokenData)
|
json_encode([
|
||||||
|
'client_id' => $authTokenData['client_id'],
|
||||||
|
'auth_token' => $authTokenData['token'],
|
||||||
|
'created_at' => now()->toIso8601String(),
|
||||||
|
])
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 記錄操作日誌
|
||||||
$this->logService->logOperation(
|
$this->logService->logOperation(
|
||||||
'client',
|
'client',
|
||||||
$authToken->client_id,
|
$authTokenData['client_id'],
|
||||||
'Generated new access token'
|
'Generated new access token'
|
||||||
);
|
);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'access_token' => $accessToken,
|
'access_token' => $accessToken,
|
||||||
'expires_in' => self::ACCESS_TOKEN_TTL,
|
'expires_in' => self::ACCESS_TOKEN_TTL,
|
||||||
|
'token_type' => 'Bearer',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function validateAuthToken(string $token): ?AuthToken
|
|
||||||
{
|
|
||||||
$authToken = AuthToken::where('token', $token)->first();
|
|
||||||
|
|
||||||
if (!$authToken || !$authToken->isValid()) {
|
|
||||||
if ($authToken) {
|
|
||||||
$this->logService->logOperation(
|
|
||||||
'client',
|
|
||||||
$authToken->client_id,
|
|
||||||
'Invalid auth token attempt'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $authToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function validateAccessToken(string $token): ?array
|
public function validateAccessToken(string $token): ?array
|
||||||
{
|
{
|
||||||
$tokenData = Redis::get(self::ACCESS_TOKEN_PREFIX . $token);
|
$tokenData = Redis::get(self::ACCESS_TOKEN_PREFIX . $token);
|
||||||
|
|
||||||
if (!$tokenData) {
|
if (!$tokenData) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = json_decode($tokenData, true);
|
return json_decode($tokenData, true);
|
||||||
$expiresAt = \DateTime::createFromFormat(\DateTime::ISO8601, $data['expires_at']);
|
|
||||||
|
|
||||||
if ($expiresAt < now()) {
|
|
||||||
$this->revokeAccessToken($token);
|
|
||||||
$this->logService->logOperation(
|
|
||||||
'client',
|
|
||||||
$data['client_id'],
|
|
||||||
'Access token expired'
|
|
||||||
);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function revokeAccessToken(string $token): void
|
public function revokeAccessToken(string $token): void
|
||||||
|
|
@ -111,22 +124,51 @@ public function revokeAccessToken(string $token): void
|
||||||
$tokenData = Redis::get(self::ACCESS_TOKEN_PREFIX . $token);
|
$tokenData = Redis::get(self::ACCESS_TOKEN_PREFIX . $token);
|
||||||
if ($tokenData) {
|
if ($tokenData) {
|
||||||
$data = json_decode($tokenData, true);
|
$data = json_decode($tokenData, true);
|
||||||
$this->logService->logOperation(
|
if ($data) {
|
||||||
'client',
|
// 記錄操作日誌
|
||||||
$data['client_id'],
|
$this->logService->logOperation(
|
||||||
'Access token revoked'
|
'client',
|
||||||
);
|
$data['client_id'],
|
||||||
|
'Access token revoked'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 從 Redis 中刪除
|
||||||
|
Redis::del(self::ACCESS_TOKEN_PREFIX . $token);
|
||||||
}
|
}
|
||||||
Redis::del(self::ACCESS_TOKEN_PREFIX . $token);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function revokeAuthToken(AuthToken $authToken): void
|
public function revokeAuthToken(string $token): void
|
||||||
{
|
{
|
||||||
$this->logService->logOperation(
|
$tokenData = Redis::get(self::AUTH_TOKEN_PREFIX . $token);
|
||||||
'client',
|
if ($tokenData) {
|
||||||
$authToken->client_id,
|
$data = json_decode($tokenData, true);
|
||||||
'Auth token revoked'
|
if ($data) {
|
||||||
);
|
// 計算剩餘有效期
|
||||||
$authToken->delete();
|
$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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cleanupExpiredTokens(): void
|
||||||
|
{
|
||||||
|
// Redis 會自動清理過期的令牌,無需手動清理
|
||||||
|
// 這個方法保留用於日誌記錄或其他清理工作
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue