checkCacheHealth()) { Log::warning('缓存不可用,直接从数据库验证token'); return $this->validateTokenFromDatabase($token, $guard); } // 生成缓存键 $cacheKey = $this->getTokenCacheKey($token); // 尝试从缓存获取用户信息 try { $cachedUser = Cache::get($cacheKey); if ($cachedUser && $cachedUser instanceof User) { Log::debug('Token认证使用缓存', ['token_hash' => hash('sha256', $token)]); return $cachedUser; } } catch (Exception $e) { Log::warning('缓存读取失败,使用数据库验证', ['error' => $e->getMessage()]); } // 缓存未命中或失败,从数据库查询 $user = $this->validateTokenFromDatabase($token, $guard); if ($user && $this->cacheAvailable) { try { // 计算缓存时间并存储 $cacheMinutes = $this->calculateCacheTime($token); Cache::put($cacheKey, $user, now()->addMinutes($cacheMinutes)); Log::debug('Token认证结果已缓存', [ 'user_id' => $user->id, 'cache_minutes' => $cacheMinutes, 'token_hash' => hash('sha256', $token) ]); } catch (Exception $e) { Log::warning('缓存存储失败', ['error' => $e->getMessage()]); } } return $user; } /** * 从数据库验证token * * @param string $token * @param string $guard * @return User|null */ protected function validateTokenFromDatabase(string $token, string $guard): ?User { try { // 使用Sanctum验证token $accessToken = PersonalAccessToken::findToken($token); if (!$accessToken) { return null; } // 检查token是否过期 if ($accessToken->expires_at && $accessToken->expires_at->isPast()) { return null; } // 获取token对应的用户 $user = $accessToken->tokenable; if (!$user || !($user instanceof User)) { return null; } // 检查用户状态 if ($user->status != 0) { return null; } // 更新最后使用时间 $accessToken->update(['last_used_at' => now()]); return $user; } catch (\Exception $e) { Log::error('Token验证失败', [ 'error' => $e->getMessage(), 'token_hash' => hash('sha256', $token) ]); return null; } } /** * 计算缓存时间 * 根据token过期时间来决定缓存时间 * * @param string $token * @return int 缓存分钟数 */ protected function calculateCacheTime(string $token): int { try { $accessToken = PersonalAccessToken::findToken($token); if (!$accessToken || !$accessToken->expires_at) { return self::DEFAULT_CACHE_MINUTES; } // 计算token剩余时间(分钟) $remainingMinutes = now()->diffInMinutes($accessToken->expires_at, false); if ($remainingMinutes <= 0) { return 1; // token即将过期,缓存1分钟 } // 取剩余时间的1/4作为缓存时间,但不超过最大缓存时间 $cacheMinutes = min( max(1, floor($remainingMinutes / 4)), self::MAX_CACHE_MINUTES ); return (int) $cacheMinutes; } catch (\Exception $e) { Log::warning('计算缓存时间失败', ['error' => $e->getMessage()]); return self::DEFAULT_CACHE_MINUTES; } } /** * 清空指定token的缓存 * * @param string $token * @return bool */ public function clearTokenCache(string $token): bool { $cacheKey = $this->getTokenCacheKey($token); $result = Cache::forget($cacheKey); Log::info('清空Token缓存', [ 'token_hash' => hash('sha256', $token), 'success' => $result ]); return $result; } /** * 清空指定用户的所有token缓存 * * @param int $userId * @return int 清空的缓存数量 */ public function clearUserTokenCache(int $userId): int { $user = User::find($userId); if (!$user) { return 0; } $clearedCount = 0; // 由于无法直接获取原始token,这里使用模式匹配清空缓存 // 实际应用中建议维护一个用户token的映射关系 try { // 获取所有以用户ID结尾的token缓存键(这是一个简化的方案) // 更好的方案是在缓存时同时维护用户->token的映射 $pattern = self::CACHE_PREFIX . '*'; // 注意:这种方法效率不高,建议在实际应用中优化 // 可以考虑维护一个用户token列表的缓存 Log::info('清空用户Token缓存(注意:当前实现会在用户登出时调用clearCacheOnLogout方法)', [ 'user_id' => $userId, 'note' => '建议使用clearCacheOnLogout方法传入具体token' ]); } catch (\Exception $e) { Log::error('清空用户Token缓存失败', [ 'user_id' => $userId, 'error' => $e->getMessage() ]); } return $clearedCount; } /** * 清空所有认证相关缓存 * * @return bool */ public function clearAllAuthCache(): bool { try { // 清空所有token相关缓存 $tags = ['auth_tokens']; Cache::tags($tags)->flush(); Log::info('清空所有认证缓存'); return true; } catch (\Exception $e) { Log::error('清空所有认证缓存失败', ['error' => $e->getMessage()]); return false; } } /** * 在用户登出时清空token缓存 * * @param User $user * @param string|null $currentToken 当前token,如果提供则只清空当前token * @return int 清空的缓存数量 */ public function clearCacheOnLogout(User $user, ?string $currentToken = null): int { if ($currentToken) { // 只清空当前token缓存 return $this->clearTokenCache($currentToken) ? 1 : 0; } // 清空用户所有token缓存 return $this->clearUserTokenCache($user->id); } /** * 获取token缓存键 * * @param string $token * @return string */ protected function getTokenCacheKey(string $token): string { return self::CACHE_PREFIX . hash('sha256', $token); } /** * 获取用户缓存键 * * @param int $userId * @return string */ protected function getUserCacheKey(int $userId): string { return self::USER_CACHE_PREFIX . $userId; } /** * 检查缓存健康状态 * * @return bool */ protected function checkCacheHealth(): bool { if (!$this->cacheAvailable) { return false; } try { // 测试缓存连接 $testKey = 'cache_health_check_' . time(); $testValue = 'test'; Cache::put($testKey, $testValue, 60); $result = Cache::get($testKey); Cache::forget($testKey); $isHealthy = ($result === $testValue); if (!$isHealthy) { $this->cacheAvailable = false; Log::error('缓存健康检查失败:读写测试不一致'); } return $isHealthy; } catch (Exception $e) { $this->cacheAvailable = false; Log::error('缓存健康检查失败', ['error' => $e->getMessage()]); return false; } } /** * 获取缓存统计信息 * * @return array */ public function getCacheStats(): array { $health = $this->checkCacheHealth(); return [ 'cache_prefix' => self::CACHE_PREFIX, 'default_cache_minutes' => self::DEFAULT_CACHE_MINUTES, 'max_cache_minutes' => self::MAX_CACHE_MINUTES, 'cache_available' => $this->cacheAvailable, 'cache_health' => $health, 'cache_store' => config('cache.default'), ]; } }