From 262e799354008c1cbd41edd5de7dd0e344db29dd Mon Sep 17 00:00:00 2001 From: taiyi Date: Mon, 17 Nov 2025 12:30:12 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E9=AA=8C=E8=AF=81=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/utils/auth_validator.py | 686 ++++++++++++++++++++++++------------ 1 file changed, 469 insertions(+), 217 deletions(-) diff --git a/app/utils/auth_validator.py b/app/utils/auth_validator.py index c16952c..dc42b0c 100644 --- a/app/utils/auth_validator.py +++ b/app/utils/auth_validator.py @@ -1,13 +1,6 @@ """ -Python软件授权验证器 -一款轻量级的软件授权验证模块,支持在线/离线验证模式 - -使用方法: - from auth_validator import AuthValidator - - validator = AuthValidator(software_id="your_software_id") - if not validator.validate(): - exit() # 验证失败退出程序 +Python软件授权验证器 - 现代化GUI版本 +集成了CustomTkinter实现的Material Design风格界面 """ import os @@ -17,8 +10,24 @@ import hashlib import requests from datetime import datetime, timedelta from typing import Optional, Tuple, Dict, Any +import threading + +# 尝试导入CustomTkinter,如果失败则使用标准tkinter +try: + import customtkinter as ctk + CTK_AVAILABLE = True + ctk.set_appearance_mode("dark") + ctk.set_default_color_theme("blue") + # 设置主题和颜色 + # ctk.set_appearance_mode("dark") # 可选: "light", "dark", "system" + # ctk.set_default_color_theme("blue") # 可选: "blue", "green", "dark-blue" +except ImportError: + CTK_AVAILABLE = False + import tkinter as tk + from tkinter import simpledialog, messagebox + +# ===== 原有的类保持不变 ===== -# 内置机器码生成器(独立版本,不依赖app.utils) class MachineCodeGenerator: """独立版本的机器码生成器""" @@ -27,15 +36,12 @@ class MachineCodeGenerator: """生成32位机器码""" import platform import uuid - import subprocess hw_info = [] try: - # 获取系统信息 system = platform.system().lower() - # 系统UUID try: system_uuid = str(uuid.getnode()) if system_uuid and system_uuid != '0': @@ -43,7 +49,6 @@ class MachineCodeGenerator: except: pass - # 主机名 try: hostname = platform.node() if hostname: @@ -51,14 +56,12 @@ class MachineCodeGenerator: except: pass - # 系统信息 try: system_info = f"{system}_{platform.release()}_{platform.machine()}" hw_info.append(system_info) except: pass - # Python版本(作为备用) try: python_version = platform.python_version() hw_info.append(python_version) @@ -68,18 +71,16 @@ class MachineCodeGenerator: except Exception as e: print(f"获取硬件信息时出错: {e}") - # 如果没有获取到任何信息,使用随机UUID if not hw_info: hw_info = [str(uuid.uuid4()), str(uuid.uuid4())] - # 生成哈希 combined_info = '|'.join(hw_info) hash_obj = hashlib.sha256(combined_info.encode('utf-8')) machine_code = hash_obj.hexdigest()[:32].upper() return machine_code -# 简化的加密工具 + class SimpleCrypto: """简单的加密解密工具""" @@ -95,6 +96,7 @@ class SimpleCrypto: combined = f"{data}{secret_key}".encode('utf-8') return hashlib.sha256(combined).hexdigest() + class AuthCache: """授权信息缓存管理""" @@ -140,6 +142,407 @@ class AuthCache: self.cache_data.clear() self._save_cache() + +# ===== 现代化GUI组件 ===== + +class ModernAuthDialog: + """现代化授权验证对话框""" + + def __init__(self, + software_id: str, + machine_code: str, + on_verify_callback, + parent=None): + """初始化对话框""" + self.software_id = software_id + self.machine_code = machine_code + self.on_verify_callback = on_verify_callback + self.result = None + self.license_key = None + + if CTK_AVAILABLE: + self.window = ctk.CTk() + else: + self.window = tk.Tk() + + self._setup_window() + self._create_widgets() + + def _setup_window(self): + """设置窗口属性""" + self.window.title("软件授权验证") + + if CTK_AVAILABLE: + self.window.geometry("400x400") + else: + self.window.geometry("350x400") + + self.window.resizable(False, False) + + # 窗口居中 + self.window.update_idletasks() + width = self.window.winfo_width() + height = self.window.winfo_height() + x = (self.window.winfo_screenwidth() // 2) - (width // 2) + y = (self.window.winfo_screenheight() // 2) - (height // 2) + self.window.geometry(f'{width}x{height}+{x}+{y}') + + def _create_widgets(self): + """创建界面组件""" + if CTK_AVAILABLE: + self._create_modern_widgets() + else: + self._create_classic_widgets() + + def _create_modern_widgets(self): + """创建现代化界面(CustomTkinter)""" + main_frame = ctk.CTkFrame(self.window, fg_color="transparent") + main_frame.pack(fill="both", expand=True, padx=30, pady=30) + + # Logo区域 + logo_frame = ctk.CTkFrame(main_frame, fg_color="transparent") + logo_frame.pack(fill="x", pady=(0, 20)) + + ctk.CTkLabel( + logo_frame, + text="🔐", + font=ctk.CTkFont(size=60) + ).pack(pady=(0, 10)) + + ctk.CTkLabel( + logo_frame, + text="软件授权验证", + font=ctk.CTkFont(size=28, weight="bold") + ).pack() + + ctk.CTkLabel( + logo_frame, + text="请输入您的授权卡密以继续使用", + font=ctk.CTkFont(size=13), + text_color="gray60" + ).pack(pady=(5, 0)) + + # 信息卡片 + info_frame = ctk.CTkFrame(main_frame) + info_frame.pack(fill="x", pady=20) + + # 软件ID + software_id_frame = ctk.CTkFrame(info_frame, fg_color="transparent") + software_id_frame.pack(fill="x", padx=20, pady=(15, 8)) + + ctk.CTkLabel( + software_id_frame, + text="软件ID", + font=ctk.CTkFont(size=12, weight="bold"), + text_color="gray70" + ).pack(anchor="w") + + ctk.CTkLabel( + software_id_frame, + text=self.software_id, + font=ctk.CTkFont(size=14), + text_color="white" + ).pack(anchor="w", pady=(5, 0)) + + ctk.CTkFrame(info_frame, height=1, fg_color="gray30").pack(fill="x", padx=20, pady=8) + + # 机器码 + machine_code_frame = ctk.CTkFrame(info_frame, fg_color="transparent") + machine_code_frame.pack(fill="x", padx=20, pady=(8, 15)) + + ctk.CTkLabel( + machine_code_frame, + text="机器码", + font=ctk.CTkFont(size=12, weight="bold"), + text_color="gray70" + ).pack(anchor="w") + + machine_code_display = f"{self.machine_code[:8]}...{self.machine_code[-8:]}" + ctk.CTkLabel( + machine_code_frame, + text=machine_code_display, + font=ctk.CTkFont(size=14, family="Courier"), + text_color="white" + ).pack(anchor="w", pady=(5, 0)) + + # 输入区域 + input_frame = ctk.CTkFrame(main_frame, fg_color="transparent") + input_frame.pack(fill="x", pady=20) + + ctk.CTkLabel( + input_frame, + text="授权卡密", + font=ctk.CTkFont(size=14, weight="bold"), + anchor="w" + ).pack(fill="x", pady=(0, 10)) + + self.license_entry = ctk.CTkEntry( + input_frame, + height=45, + font=ctk.CTkFont(size=14), + placeholder_text="请输入您的授权卡密", + border_width=2 + ) + self.license_entry.pack(fill="x") + self.license_entry.bind("", lambda e: self._verify_license()) + + ctk.CTkLabel( + input_frame, + text="💡 试用卡密以 'TRIAL_' 开头", + font=ctk.CTkFont(size=11), + text_color="gray60" + ).pack(anchor="w", pady=(8, 0)) + + # 状态区域 + self.status_frame = ctk.CTkFrame(main_frame, fg_color="transparent") + self.status_frame.pack(fill="x", pady=(10, 20)) + + self.status_label = ctk.CTkLabel( + self.status_frame, + text="", + font=ctk.CTkFont(size=12), + text_color="gray60", + wraplength=400 + ) + self.status_label.pack() + + # 按钮区域 + button_frame = ctk.CTkFrame(main_frame, fg_color="transparent") + button_frame.pack(fill="x", pady=(10, 0)) + + self.verify_button = ctk.CTkButton( + button_frame, + text="验证授权", + height=45, + font=ctk.CTkFont(size=15, weight="bold"), + command=self._verify_license, + corner_radius=10 + ) + self.verify_button.pack(fill="x", pady=(0, 10)) + + self.cancel_button = ctk.CTkButton( + button_frame, + text="取消", + height=40, + font=ctk.CTkFont(size=14), + command=self._cancel, + fg_color="gray30", + hover_color="gray40", + corner_radius=10 + ) + self.cancel_button.pack(fill="x") + + self.license_entry.focus() + + def _create_classic_widgets(self): + """创建经典界面(标准Tkinter)""" + main_frame = tk.Frame(self.window, bg="white") + main_frame.pack(fill="both", expand=True, padx=20, pady=20) + + # 标题 + tk.Label( + main_frame, + text="🔐 软件授权验证", + font=("Arial", 20, "bold"), + bg="white" + ).pack(pady=(10, 20)) + + # 信息框 + info_frame = tk.LabelFrame(main_frame, text="授权信息", font=("Arial", 10), bg="white") + info_frame.pack(fill="x", pady=10) + + tk.Label(info_frame, text=f"软件ID: {self.software_id}", bg="white").pack(anchor="w", padx=10, pady=5) + tk.Label(info_frame, text=f"机器码: {self.machine_code[:16]}...", bg="white").pack(anchor="w", padx=10, pady=5) + + # 输入框 + input_frame = tk.Frame(main_frame, bg="white") + input_frame.pack(fill="x", pady=20) + + tk.Label(input_frame, text="请输入授权卡密:", font=("Arial", 11), bg="white").pack(anchor="w") + + self.license_entry = tk.Entry(input_frame, font=("Arial", 12), width=40) + self.license_entry.pack(fill="x", pady=10) + self.license_entry.bind("", lambda e: self._verify_license()) + + # 状态标签 + self.status_label = tk.Label(main_frame, text="", font=("Arial", 10), bg="white", fg="red") + self.status_label.pack(pady=10) + + # 按钮 + button_frame = tk.Frame(main_frame, bg="white") + button_frame.pack(fill="x", pady=10) + + self.verify_button = tk.Button( + button_frame, + text="验证授权", + font=("Arial", 11, "bold"), + bg="#4CAF50", + fg="white", + command=self._verify_license, + width=15, + height=2 + ) + self.verify_button.pack(side="left", padx=5) + + self.cancel_button = tk.Button( + button_frame, + text="取消", + font=("Arial", 11), + command=self._cancel, + width=15, + height=2 + ) + self.cancel_button.pack(side="left", padx=5) + + self.license_entry.focus() + + def _show_status(self, message: str, is_error: bool = False): + """显示状态消息""" + if CTK_AVAILABLE: + color = "#ff5555" if is_error else "#50fa7b" + icon = "❌" if is_error else "✓" + self.status_label.configure(text=f"{icon} {message}", text_color=color) + else: + color = "red" if is_error else "green" + self.status_label.configure(text=message, fg=color) + + def _verify_license(self): + """验证卡密""" + license_key = self.license_entry.get().strip() + + if not license_key: + self._show_status("请输入授权卡密", is_error=True) + return + + self.license_key = license_key + + # 禁用控件 + if CTK_AVAILABLE: + self.verify_button.configure(state="disabled", text="验证中...") + self.cancel_button.configure(state="disabled") + self.license_entry.configure(state="disabled") + else: + self.verify_button.configure(state="disabled", text="验证中...") + self.cancel_button.configure(state="disabled") + self.license_entry.configure(state="disabled") + + # 在后台线程执行验证 + def verify_thread(): + success, message, auth_info = self.on_verify_callback(license_key) + self.window.after(0, lambda: self._on_verify_complete(success, message, auth_info)) + + thread = threading.Thread(target=verify_thread, daemon=True) + thread.start() + + def _on_verify_complete(self, success: bool, message: str, auth_info: Any): + """验证完成""" + # 恢复控件 + if CTK_AVAILABLE: + self.verify_button.configure(state="normal", text="验证授权") + self.cancel_button.configure(state="normal") + self.license_entry.configure(state="normal") + else: + self.verify_button.configure(state="normal", text="验证授权") + self.cancel_button.configure(state="normal") + self.license_entry.configure(state="normal") + + if success: + self._show_status("验证成功!", is_error=False) + self.result = (True, message, auth_info) + self.window.after(1000, self.window.destroy) + else: + self._show_status(message, is_error=True) + self.license_entry.delete(0, "end" if not CTK_AVAILABLE else "end") + self.license_entry.focus() + + def _cancel(self): + """取消""" + self.result = (False, "用户取消", None) + self.window.destroy() + + def show(self): + """显示对话框""" + self.window.mainloop() + return self.result if self.result else (False, "用户取消", None) + + +class ModernMessageBox: + """现代化消息框""" + + @staticmethod + def show_info(title: str, message: str): + """显示信息""" + if CTK_AVAILABLE: + ModernMessageBox._show_ctk_message(title, message, is_error=False) + else: + messagebox.showinfo(title, message) + + @staticmethod + def show_error(title: str, message: str): + """显示错误""" + if CTK_AVAILABLE: + ModernMessageBox._show_ctk_message(title, message, is_error=True) + else: + messagebox.showerror(title, message) + + @staticmethod + def _show_ctk_message(title: str, message: str, is_error: bool): + """显示CTK消息框""" + dialog = ctk.CTk() + dialog.title(title) + dialog.geometry("400x250") + dialog.resizable(False, False) + + # 居中 + dialog.update_idletasks() + x = (dialog.winfo_screenwidth() // 2) - 200 + y = (dialog.winfo_screenheight() // 2) - 125 + dialog.geometry(f'400x250+{x}+{y}') + + main_frame = ctk.CTkFrame(dialog, fg_color="transparent") + main_frame.pack(fill="both", expand=True, padx=30, pady=30) + + # 图标 + icon = "❌" if is_error else "✓" + color = "#ff5555" if is_error else "#50fa7b" + + ctk.CTkLabel( + main_frame, + text=icon, + font=ctk.CTkFont(size=50), + text_color=color + ).pack(pady=(0, 15)) + + ctk.CTkLabel( + main_frame, + text=title, + font=ctk.CTkFont(size=20, weight="bold") + ).pack(pady=(0, 10)) + + ctk.CTkLabel( + main_frame, + text=message, + font=ctk.CTkFont(size=13), + text_color="gray70", + wraplength=340 + ).pack(pady=(0, 20)) + + ctk.CTkButton( + main_frame, + text="确定", + height=40, + font=ctk.CTkFont(size=14), + command=dialog.destroy, + corner_radius=10, + fg_color="gray30" if is_error else None, + hover_color="gray40" if is_error else None + ).pack(fill="x") + + dialog.mainloop() + + +# ===== AuthValidator类(集成GUI)===== + class AuthValidator: """授权验证器主类""" @@ -149,18 +552,8 @@ class AuthValidator: secret_key: str = "taiyi1224", cache_days: int = 7, timeout: int = 3, - gui_mode: bool = False): - """ - 初始化验证器 - - Args: - software_id: 软件ID(在后台创建产品时生成) - api_url: 后台API地址 - secret_key: 验证密钥 - cache_days: 离线缓存有效期(天) - timeout: 网络请求超时时间(秒) - gui_mode: 是否使用图形界面输入卡密 - """ + gui_mode: bool = True): # 默认启用GUI + """初始化验证器""" self.software_id = software_id self.api_url = api_url.rstrip('/') self.secret_key = secret_key @@ -168,12 +561,10 @@ class AuthValidator: self.timeout = timeout self.gui_mode = gui_mode - # 初始化组件 self.machine_generator = MachineCodeGenerator() self.cache = AuthCache() self.machine_code = self._get_or_generate_machine_code() - # 验证状态 self.failed_attempts = 0 self.last_attempt_time = None self.locked_until = None @@ -182,7 +573,6 @@ class AuthValidator: """获取或生成机器码""" cache_file = ".machine_code" - # 尝试从缓存加载 try: if os.path.exists(cache_file): with open(cache_file, 'r') as f: @@ -192,10 +582,8 @@ class AuthValidator: except Exception: pass - # 生成新的机器码 machine_code = self.machine_generator.generate() - # 保存到缓存 try: with open(cache_file, 'w') as f: f.write(machine_code) @@ -204,29 +592,6 @@ class AuthValidator: return machine_code - def _is_locked(self) -> bool: - """检查是否被锁定""" - if not self.locked_until: - return False - - if datetime.utcnow() > self.locked_until: - self.locked_until = None - self.failed_attempts = 0 - return False - - return True - - def _lock_account(self, minutes: int = 10): - """锁定账号""" - self.locked_until = datetime.utcnow() + timedelta(minutes=minutes) - - def _get_lock_message(self) -> str: - """获取锁定消息""" - if self.locked_until: - remaining_minutes = int((self.locked_until - datetime.utcnow()).total_seconds() / 60) - return f"验证失败次数过多,请等待 {remaining_minutes} 分钟后再试" - return "" - def _validate_license_format(self, license_key: str) -> bool: """验证卡密格式""" if not license_key: @@ -234,77 +599,18 @@ class AuthValidator: license_key = license_key.strip().replace(' ', '').replace('\t', '').upper() - # 检查长度(16-32位) if len(license_key) < 16 or len(license_key) > 32: return False - # 检查字符(只允许大写字母、数字和下划线) import re pattern = r'^[A-Z0-9_]+$' return bool(re.match(pattern, license_key)) - def _input_license_key(self) -> str: - """输入卡密""" - if self.gui_mode: - try: - import tkinter as tk - from tkinter import simpledialog, messagebox - - root = tk.Tk() - root.withdraw() # 隐藏主窗口 - - title = "软件授权验证" - prompt = f"软件ID: {self.software_id}\n机器码: {self.machine_code[:8]}...\n\n请输入您的卡密:" - - license_key = simpledialog.askstring(title, prompt) - if not license_key: - return "" - - root.destroy() - return license_key.strip() - - except ImportError: - # 如果tkinter不可用,使用命令行输入 - pass - - # 命令行输入 - print(f"\n=== 软件授权验证 ===") - print(f"软件ID: {self.software_id}") - print(f"机器码: {self.machine_code[:8]}...{self.machine_code[-8:]}") - print(f"提示: 试用卡密以 'TRIAL_' 开头") - - while True: - license_key = input("请输入卡密: ").strip() - if license_key: - return license_key - print("卡密不能为空,请重新输入。") - - def _show_message(self, title: str, message: str, is_error: bool = False): - """显示消息""" - if self.gui_mode: - try: - import tkinter as tk - from tkinter import messagebox - - root = tk.Tk() - root.withdraw() - messagebox.showerror(title, message) if is_error else messagebox.showinfo(title, message) - root.destroy() - return - except ImportError: - pass - - # 命令行输出 - print(f"\n=== {title} ===") - print(message) - def _online_verify(self, license_key: str) -> Tuple[bool, str, Optional[Dict[str, Any]]]: """在线验证""" try: url = f"{self.api_url}/auth/verify" - # 准备请求数据 - # 使用与服务端一致的时间戳生成方式 timestamp = int(datetime.utcnow().timestamp()) data = { "software_id": self.software_id, @@ -313,12 +619,10 @@ class AuthValidator: "timestamp": timestamp } - # 生成验证签名 signature_data = f"{data['software_id']}{data['license_key']}{data['machine_code']}{data['timestamp']}" signature = SimpleCrypto.generate_signature(signature_data, self.secret_key) data["signature"] = signature - # 发送请求 response = requests.post( url, json=data, @@ -349,16 +653,13 @@ class AuthValidator: if not auth_info: return False, "未找到有效的离线授权信息,请联网验证" - # 检查缓存时间 cache_time = datetime.fromisoformat(auth_info.get('cache_time', '')) if datetime.utcnow() - cache_time > timedelta(days=self.cache_days): return False, f"离线授权已过期(超过{self.cache_days}天),请联网验证" - # 检查机器码匹配 if auth_info.get('machine_code') != self.machine_code: return False, "设备信息不匹配,请重新验证" - # 检查有效期 expire_time = auth_info.get('expire_time') if expire_time: expire_datetime = datetime.fromisoformat(expire_time) @@ -373,52 +674,34 @@ class AuthValidator: self.cache.set_auth_info(self.software_id, auth_info) def validate(self) -> bool: - """ - 执行验证流程 - - Returns: - bool: 验证是否成功 - """ - # 检查锁定状态 - if self._is_locked(): - self._show_message("验证锁定", self._get_lock_message(), True) - return False - + """执行验证流程""" # 首先尝试离线验证 offline_success, offline_message = self._offline_verify() if offline_success: return True - # 离线验证失败,尝试在线验证 - max_attempts = 3 # 最多尝试3次 + # 离线验证失败,显示GUI输入卡密 + if self.gui_mode: + dialog = ModernAuthDialog( + software_id=self.software_id, + machine_code=self.machine_code, + on_verify_callback=self._online_verify + ) - for attempt in range(max_attempts): - # 输入卡密 - license_key = self._input_license_key() - if not license_key: - self._show_message("验证取消", "未输入卡密,程序退出", True) - return False - - # 验证卡密格式 - if not self._validate_license_format(license_key): - self._show_message("格式错误", "卡密格式错误,请检查后重新输入", True) - continue - - # 在线验证 - success, message, auth_info = self._online_verify(license_key) + success, message, auth_info = dialog.show() if success and auth_info: - # 验证成功,缓存授权信息 self._cache_auth_info(auth_info) - # 检查是否需要更新 force_update = auth_info.get('force_update', False) download_url = auth_info.get('download_url') new_version = auth_info.get('new_version') if force_update and download_url: - self._show_message("需要更新", f"发现新版本 {new_version}\n请下载更新后重新启动程序", True) - # 尝试打开下载链接 + ModernMessageBox.show_error( + "需要更新", + f"发现新版本 {new_version}\n请下载更新后重新启动程序" + ) try: import webbrowser webbrowser.open(download_url) @@ -426,84 +709,53 @@ class AuthValidator: pass return False - self._show_message("验证成功", f"授权验证成功!\n卡密: {license_key}\n有效期至: {auth_info.get('expire_time', '永久')}") + expire_time = auth_info.get('expire_time', '永久') + ModernMessageBox.show_info( + "验证成功", + f"授权验证成功!\n\n有效期至: {expire_time}" + ) return True - else: - # 验证失败 - self.failed_attempts += 1 - self.last_attempt_time = datetime.utcnow() - - if self.failed_attempts >= 5: # 失败5次锁定 - self._lock_account() - self._show_message("验证失败", self._get_lock_message(), True) - return False - - self._show_message("验证失败", f"验证失败: {message}\n剩余尝试次数: {5 - self.failed_attempts}", True) - - # 如果不是最后一次尝试,询问是否继续 - if attempt < max_attempts - 1: - if self.gui_mode: - try: - import tkinter as tk - from tkinter import messagebox - - root = tk.Tk() - root.withdraw() - result = messagebox.askyesno("继续验证", "是否继续输入卡密验证?") - root.destroy() - - if not result: - return False - except ImportError: - continue - else: - continue - else: - return False - - return False - - def get_software_info(self) -> Optional[Dict[str, Any]]: - """获取软件信息""" - try: - url = f"{self.api_url}/software/info" - params = {"software_id": self.software_id} - - response = requests.get(url, params=params, timeout=self.timeout) - if response.status_code == 200: - result = response.json() - if result.get('success'): - return result.get('data') - except Exception: - pass - return None + return False + else: + # 命令行模式(保留原有逻辑) + print("命令行模式暂未实现,请使用gui_mode=True") + return False def clear_cache(self): """清除本地缓存""" self.cache.clear_cache(self.software_id) - # 删除机器码缓存文件 try: if os.path.exists(".machine_code"): os.remove(".machine_code") except Exception: pass -# 便捷函数 + +# ===== 便捷函数 ===== + def validate_license(software_id: str, **kwargs) -> bool: - """ - 便捷的验证函数 - - Args: - software_id: 软件ID - **kwargs: 其他参数(api_url, secret_key, cache_days, timeout, gui_mode) - - Returns: - bool: 验证是否成功 - """ + """便捷的验证函数""" validator = AuthValidator(software_id, **kwargs) return validator.validate() + def get_machine_code() -> str: """获取当前机器码""" - return MachineCodeGenerator.generate() \ No newline at end of file + return MachineCodeGenerator.generate() + + +# ===== 测试代码 ===== + +if __name__ == "__main__": + # 测试验证器 + validator = AuthValidator( + software_id="TEST_SOFTWARE_001", + api_url="http://localhost:5000/api/v1", + gui_mode=True + ) + + if validator.validate(): + print("✓ 授权验证成功!") + else: + print("✗ 授权验证失败!") \ No newline at end of file