token system fix
This commit is contained in:
parent
33dde9dc2c
commit
4b1ed6ade3
3 changed files with 120 additions and 34 deletions
|
|
@ -15,6 +15,7 @@
|
|||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
|
||||
class ClientController extends Controller
|
||||
{
|
||||
|
|
@ -273,35 +274,109 @@ public function update(Request $request, int $id): JsonResponse
|
|||
}
|
||||
|
||||
/**
|
||||
* 删除客户
|
||||
*
|
||||
* @param Request $request
|
||||
* @param int $id
|
||||
* @return JsonResponse
|
||||
* 檢查客戶是否有活躍的令牌
|
||||
*/
|
||||
public function destroy(Request $request, int $id): JsonResponse
|
||||
private function hasActiveTokens(Client $client): bool
|
||||
{
|
||||
$pattern = 'auth_token:*';
|
||||
$keys = Redis::keys($pattern);
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$data = Redis::get($key);
|
||||
if ($data) {
|
||||
$tokenData = json_decode($data, true);
|
||||
if ($tokenData &&
|
||||
$tokenData['client_id'] === $client->id &&
|
||||
now()->isBefore($tokenData['expires_at'])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 停用客戶
|
||||
*/
|
||||
public function deactivate(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
$client = Client::findOrFail($id);
|
||||
|
||||
// 检查是否有活跃的认证令牌
|
||||
if ($client->authTokens()->where('status', 'active')->exists()) {
|
||||
if (!$this->admin->canManageClient($client->id)) {
|
||||
return $this->error(
|
||||
ErrorCode::CLIENT_HAS_ACTIVE_TOKENS,
|
||||
'该客户有活跃的认证令牌,请先停用或删除令牌。'
|
||||
ErrorCode::FORBIDDEN,
|
||||
'您無權管理該客戶。'
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->hasActiveTokens($client)) {
|
||||
return $this->error(
|
||||
ErrorCode::CLIENT_HAS_ACTIVE_TOKENS,
|
||||
'該客戶有活躍的認證令牌,無法停用。'
|
||||
);
|
||||
}
|
||||
|
||||
$client->update(['status' => Client::STATUS_INACTIVE]);
|
||||
|
||||
$this->logService->logOperation(
|
||||
'admin',
|
||||
$this->admin->id,
|
||||
"Deactivated client: {$client->name}"
|
||||
);
|
||||
|
||||
return $this->success();
|
||||
|
||||
} catch (ModelNotFoundException $e) {
|
||||
return $this->error(
|
||||
ErrorCode::CLIENT_NOT_FOUND,
|
||||
ErrorCode::getMessage(ErrorCode::CLIENT_NOT_FOUND)
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Error deactivating client', [
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
]);
|
||||
|
||||
return $this->error(
|
||||
ErrorCode::SERVER_ERROR,
|
||||
ErrorCode::getMessage(ErrorCode::SERVER_ERROR)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 刪除客戶
|
||||
*/
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
$client = Client::findOrFail($id);
|
||||
|
||||
if (!$this->admin->canManageClient($client->id)) {
|
||||
return $this->error(
|
||||
ErrorCode::FORBIDDEN,
|
||||
'您無權管理該客戶。'
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->hasActiveTokens($client)) {
|
||||
return $this->error(
|
||||
ErrorCode::CLIENT_HAS_ACTIVE_TOKENS,
|
||||
'該客戶有活躍的認證令牌,無法刪除。'
|
||||
);
|
||||
}
|
||||
|
||||
$clientName = $client->name;
|
||||
$client->delete();
|
||||
|
||||
$this->logService->logOperation(
|
||||
'admin',
|
||||
$request->admin->id,
|
||||
"Deleted client: {$clientName}"
|
||||
$this->admin->id,
|
||||
"Deleted client: {$client->name}"
|
||||
);
|
||||
|
||||
return $this->success(null, '客户已删除。');
|
||||
return $this->success();
|
||||
|
||||
} catch (ModelNotFoundException $e) {
|
||||
return $this->error(
|
||||
|
|
@ -312,7 +387,6 @@ public function destroy(Request $request, int $id): JsonResponse
|
|||
Log::error('Error deleting client', [
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
'client_id' => $id,
|
||||
]);
|
||||
|
||||
return $this->error(
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class Client extends Model
|
||||
{
|
||||
|
|
@ -52,11 +51,6 @@ public function admins(): BelongsToMany
|
|||
->withTimestamps();
|
||||
}
|
||||
|
||||
public function authTokens(): HasMany
|
||||
{
|
||||
return $this->hasMany(AuthToken::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查客户是否处于活跃状态
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
namespace App\Services\Auth;
|
||||
|
||||
use App\Models\AuthToken;
|
||||
use App\Models\Client;
|
||||
use App\Services\LogService;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
|
|
@ -22,7 +21,12 @@ public function __construct(
|
|||
private readonly LogService $logService
|
||||
) {}
|
||||
|
||||
public function generateAuthToken(Client $client, ?int $expiresInHours = null): AuthToken
|
||||
/**
|
||||
* 生成認證令牌
|
||||
*
|
||||
* @return array{token: string, expires_at: string}
|
||||
*/
|
||||
public function generateAuthToken(Client $client, ?int $expiresInHours = null): array
|
||||
{
|
||||
// 確保不超過最大有效期
|
||||
$effectiveHours = min($expiresInHours ?? self::MAX_AUTH_TOKEN_HOURS, self::MAX_AUTH_TOKEN_HOURS);
|
||||
|
|
@ -38,6 +42,7 @@ public function generateAuthToken(Client $client, ?int $expiresInHours = null):
|
|||
$effectiveSeconds,
|
||||
json_encode([
|
||||
'client_id' => $client->id,
|
||||
'token' => $token,
|
||||
'created_at' => now()->toIso8601String(),
|
||||
'expires_at' => $expiresAt->toIso8601String(),
|
||||
])
|
||||
|
|
@ -50,14 +55,17 @@ public function generateAuthToken(Client $client, ?int $expiresInHours = null):
|
|||
'Generated new auth token'
|
||||
);
|
||||
|
||||
// 返回令牌對象(僅用於 API 響應)
|
||||
return new AuthToken([
|
||||
'client_id' => $client->id,
|
||||
return [
|
||||
'token' => $token,
|
||||
'expires_at' => $expiresAt,
|
||||
]);
|
||||
'expires_at' => $expiresAt->toIso8601String(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 驗證認證令牌
|
||||
*
|
||||
* @return array{client_id: int, token: string, created_at: string, expires_at: string}|null
|
||||
*/
|
||||
public function validateAuthToken(string $token): ?array
|
||||
{
|
||||
// 檢查黑名單
|
||||
|
|
@ -80,6 +88,11 @@ public function validateAuthToken(string $token): ?array
|
|||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成訪問令牌
|
||||
*
|
||||
* @return array{access_token: string, expires_in: int, token_type: string}
|
||||
*/
|
||||
public function generateAccessToken(array $authTokenData): array
|
||||
{
|
||||
$accessToken = Str::random(64);
|
||||
|
|
@ -109,6 +122,11 @@ public function generateAccessToken(array $authTokenData): array
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 驗證訪問令牌
|
||||
*
|
||||
* @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);
|
||||
|
|
@ -119,6 +137,9 @@ public function validateAccessToken(string $token): ?array
|
|||
return json_decode($tokenData, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤銷訪問令牌
|
||||
*/
|
||||
public function revokeAccessToken(string $token): void
|
||||
{
|
||||
$tokenData = Redis::get(self::ACCESS_TOKEN_PREFIX . $token);
|
||||
|
|
@ -137,6 +158,9 @@ public function revokeAccessToken(string $token): void
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤銷認證令牌
|
||||
*/
|
||||
public function revokeAuthToken(string $token): void
|
||||
{
|
||||
$tokenData = Redis::get(self::AUTH_TOKEN_PREFIX . $token);
|
||||
|
|
@ -165,10 +189,4 @@ public function revokeAuthToken(string $token): void
|
|||
Redis::del(self::AUTH_TOKEN_PREFIX . $token);
|
||||
}
|
||||
}
|
||||
|
||||
public function cleanupExpiredTokens(): void
|
||||
{
|
||||
// Redis 會自動清理過期的令牌,無需手動清理
|
||||
// 這個方法保留用於日誌記錄或其他清理工作
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue