重新登录或注册后清除掉原有用户的user-token保证只有一个用户能够登录

This commit is contained in:
2026-03-05 12:25:59 +08:00
parent 74612f136e
commit 8d8cee696f
3 changed files with 62 additions and 1 deletions

View File

@@ -131,4 +131,51 @@ class UserCache
$val = Cache::get($key); $val = Cache::get($key);
return $val !== null && $val !== ''; return $val !== null && $val !== '';
} }
/** 当前有效 user-token 按用户存储的 key 前缀(重新登录/注册后覆盖,保证单用户单 token */
private static function currentTokenPrefix(): string
{
return config('api.user_token_current_prefix', 'api:user:current_token:');
}
private static function userTokenExpire(): int
{
return (int) config('api.user_token_exp', 604800);
}
/**
* 设置该用户当前唯一有效的 user-token登录/注册时调用,会覆盖该用户之前的 token
* @param int $userId 用户 ID
* @param string $token 完整 user-token 字符串
*/
public static function setCurrentUserToken(int $userId, string $token): bool
{
if ($userId <= 0 || $token === '') {
return false;
}
$key = self::currentTokenPrefix() . $userId;
return Cache::set($key, $token, self::userTokenExpire());
}
/**
* 获取该用户当前在服务端登记的有效 user-token不存在或已过期返回 null
*/
public static function getCurrentUserToken(int $userId): ?string
{
if ($userId <= 0) {
return null;
}
$key = self::currentTokenPrefix() . $userId;
$value = Cache::get($key);
return $value !== null && $value !== '' ? (string) $value : null;
}
/**
* 校验请求中的 token 是否为该用户当前唯一有效 token
*/
public static function isCurrentUserToken(int $userId, string $token): bool
{
$current = self::getCurrentUserToken($userId);
return $current !== null && $current === $token;
}
} }

View File

@@ -56,6 +56,8 @@ class UserLogic
UserCache::setUser((int) $user->id, $userArr); UserCache::setUser((int) $user->id, $userArr);
$userToken = $this->generateUserToken((int) $user->id); $userToken = $this->generateUserToken((int) $user->id);
// 同一用户只保留最新一次登录的 token旧 token 自动失效
UserCache::setCurrentUserToken((int) $user->id, $userToken);
return [ return [
'user' => $userArr, 'user' => $userArr,
'user-token' => $userToken, 'user-token' => $userToken,
@@ -94,6 +96,8 @@ class UserLogic
UserCache::setUser((int) $user->id, $userArr); UserCache::setUser((int) $user->id, $userArr);
$userToken = $this->generateUserToken((int) $user->id); $userToken = $this->generateUserToken((int) $user->id);
// 同一用户只保留最新一次登录的 token旧 token 自动失效
UserCache::setCurrentUserToken((int) $user->id, $userToken);
return [ return [
'user' => $userArr, 'user' => $userArr,
'user-token' => $userToken, 'user-token' => $userToken,
@@ -174,7 +178,15 @@ class UserLogic
return null; return null;
} }
$id = $extend['id'] ?? null; $id = $extend['id'] ?? null;
return $id !== null ? (int) $id : null; if ($id === null) {
return null;
}
$userId = (int) $id;
// 同一用户只允许当前登记的 token 生效,重新登录/注册后旧 token 失效
if (!UserCache::isCurrentUserToken($userId, $userToken)) {
return null;
}
return $userId;
} catch (\Throwable $e) { } catch (\Throwable $e) {
return null; return null;
} }

View File

@@ -13,6 +13,8 @@ return [
'auth_token_device_prefix' => env('API_AUTH_TOKEN_DEVICE_PREFIX', 'api:auth_token:'), 'auth_token_device_prefix' => env('API_AUTH_TOKEN_DEVICE_PREFIX', 'api:auth_token:'),
// user-token 有效期(秒),默认 7 天 // user-token 有效期(秒),默认 7 天
'user_token_exp' => (int) env('API_USER_TOKEN_EXP', 604800), 'user_token_exp' => (int) env('API_USER_TOKEN_EXP', 604800),
// 按用户存储当前有效 user-token 的 Redis key 前缀(同一用户仅保留最新一次登录的 token
'user_token_current_prefix' => env('API_USER_TOKEN_CURRENT_PREFIX', 'api:user:current_token:'),
// 用户信息 Redis 缓存过期时间(秒),默认 7 天 // 用户信息 Redis 缓存过期时间(秒),默认 7 天
'user_cache_expire' => (int) env('API_USER_CACHE_EXPIRE', 604800), 'user_cache_expire' => (int) env('API_USER_CACHE_EXPIRE', 604800),
// 用户缓存 Redis key 前缀 // 用户缓存 Redis key 前缀