llmbackend/app/Services/Auth/TokenService.php
2024-12-04 13:48:22 +08:00

192 lines
5.4 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
namespace App\Services\Auth;
use App\Models\Client;
use App\Services\LogService;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Str;
class TokenService
{
private const AUTH_TOKEN_PREFIX = 'auth_token:';
private const ACCESS_TOKEN_PREFIX = 'access_token:';
private const BLACKLIST_PREFIX = 'token_blacklist:';
private const MAX_AUTH_TOKEN_HOURS = 8; // 最大 8 小時有效期
private const ACCESS_TOKEN_TTL = 3600; // 1 小時有效期
public function __construct(
private readonly LogService $logService
) {}
/**
* 生成認證令牌
*
* @return array{auth_token: string, expires_at: string, client_id: int}
*/
public function generateAuthToken(Client $client, ?int $expiresInHours = null): array
{
// 確保不超過最大有效期
$effectiveHours = min($expiresInHours ?? self::MAX_AUTH_TOKEN_HOURS, self::MAX_AUTH_TOKEN_HOURS);
$effectiveSeconds = $effectiveHours * 3600;
// 生成新令牌
$token = Str::random(64);
$expiresAt = now()->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);
}
}
}