diff --git a/server/app/api/cache/AuthTokenCache.php b/server/app/api/cache/AuthTokenCache.php new file mode 100644 index 0000000..7e1d28e --- /dev/null +++ b/server/app/api/cache/AuthTokenCache.php @@ -0,0 +1,54 @@ +fail('签名验证失败', ReturnCode::EMPTY_PARAMS); } - $exp = config('api.auth_token_exp', 86400); + $exp = (int) config('api.auth_token_exp', 86400); $tokenResult = JwtToken::generateToken([ 'id' => 0, 'plat' => 'api', @@ -65,6 +66,9 @@ class AuthTokenController extends OpenController 'access_exp' => $exp, ]); + // 同一设备只保留最新 token,覆盖后旧 token 失效 + AuthTokenCache::setDeviceToken($device, $tokenResult['access_token'], $exp); + return $this->success([ 'auth-token' => $tokenResult['access_token'], 'expires_in' => $tokenResult['expires_in'], diff --git a/server/app/api/middleware/CheckAuthTokenMiddleware.php b/server/app/api/middleware/CheckAuthTokenMiddleware.php index 72503a4..efd5df6 100644 --- a/server/app/api/middleware/CheckAuthTokenMiddleware.php +++ b/server/app/api/middleware/CheckAuthTokenMiddleware.php @@ -11,6 +11,7 @@ use Tinywan\Jwt\JwtToken; use Tinywan\Jwt\Exception\JwtTokenException; use Tinywan\Jwt\Exception\JwtTokenExpiredException; use app\api\util\ReturnCode; +use app\api\cache\AuthTokenCache; use plugin\saiadmin\exception\ApiException; /** @@ -49,6 +50,12 @@ class CheckAuthTokenMiddleware implements MiddlewareInterface throw new ApiException('auth-token 无效(非 API 凭证)', ReturnCode::TOKEN_TIMEOUT); } + // 同一设备只允许一个 auth-token 生效,非当前 token 视为已失效 + $device = (string) ($extend['device'] ?? ''); + if ($device !== '' && !AuthTokenCache::isCurrentToken($device, $token)) { + throw new ApiException('auth-token 已失效(该设备已签发新凭证,请使用新 auth-token)', ReturnCode::TOKEN_TIMEOUT); + } + return $handler($request); } diff --git a/server/config/api.php b/server/config/api.php index d8fe2c1..2792834 100644 --- a/server/config/api.php +++ b/server/config/api.php @@ -9,6 +9,8 @@ return [ 'auth_token_time_tolerance' => (int) env('API_AUTH_TOKEN_TIME_TOLERANCE', 300), // auth-token 有效期(秒),默认 24 小时 'auth_token_exp' => (int) env('API_AUTH_TOKEN_EXP', 86400), + // auth-token 按设备存储的 Redis key 前缀(同一设备只保留最新一个 auth-token) + 'auth_token_device_prefix' => env('API_AUTH_TOKEN_DEVICE_PREFIX', 'api:auth_token:'), // user-token 有效期(秒),默认 7 天 'user_token_exp' => (int) env('API_USER_TOKEN_EXP', 604800), // 用户信息 Redis 缓存过期时间(秒),默认 7 天