更新验证页面

This commit is contained in:
taiyi 2025-11-17 12:30:12 +08:00
parent 1666e9cfcb
commit 262e799354

View File

@ -1,13 +1,6 @@
""" """
Python软件授权验证器 Python软件授权验证器 - 现代化GUI版本
一款轻量级的软件授权验证模块支持在线/离线验证模式 集成了CustomTkinter实现的Material Design风格界面
使用方法:
from auth_validator import AuthValidator
validator = AuthValidator(software_id="your_software_id")
if not validator.validate():
exit() # 验证失败退出程序
""" """
import os import os
@ -17,8 +10,24 @@ import hashlib
import requests import requests
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Optional, Tuple, Dict, Any 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: class MachineCodeGenerator:
"""独立版本的机器码生成器""" """独立版本的机器码生成器"""
@ -27,15 +36,12 @@ class MachineCodeGenerator:
"""生成32位机器码""" """生成32位机器码"""
import platform import platform
import uuid import uuid
import subprocess
hw_info = [] hw_info = []
try: try:
# 获取系统信息
system = platform.system().lower() system = platform.system().lower()
# 系统UUID
try: try:
system_uuid = str(uuid.getnode()) system_uuid = str(uuid.getnode())
if system_uuid and system_uuid != '0': if system_uuid and system_uuid != '0':
@ -43,7 +49,6 @@ class MachineCodeGenerator:
except: except:
pass pass
# 主机名
try: try:
hostname = platform.node() hostname = platform.node()
if hostname: if hostname:
@ -51,14 +56,12 @@ class MachineCodeGenerator:
except: except:
pass pass
# 系统信息
try: try:
system_info = f"{system}_{platform.release()}_{platform.machine()}" system_info = f"{system}_{platform.release()}_{platform.machine()}"
hw_info.append(system_info) hw_info.append(system_info)
except: except:
pass pass
# Python版本作为备用
try: try:
python_version = platform.python_version() python_version = platform.python_version()
hw_info.append(python_version) hw_info.append(python_version)
@ -68,18 +71,16 @@ class MachineCodeGenerator:
except Exception as e: except Exception as e:
print(f"获取硬件信息时出错: {e}") print(f"获取硬件信息时出错: {e}")
# 如果没有获取到任何信息使用随机UUID
if not hw_info: if not hw_info:
hw_info = [str(uuid.uuid4()), str(uuid.uuid4())] hw_info = [str(uuid.uuid4()), str(uuid.uuid4())]
# 生成哈希
combined_info = '|'.join(hw_info) combined_info = '|'.join(hw_info)
hash_obj = hashlib.sha256(combined_info.encode('utf-8')) hash_obj = hashlib.sha256(combined_info.encode('utf-8'))
machine_code = hash_obj.hexdigest()[:32].upper() machine_code = hash_obj.hexdigest()[:32].upper()
return machine_code return machine_code
# 简化的加密工具
class SimpleCrypto: class SimpleCrypto:
"""简单的加密解密工具""" """简单的加密解密工具"""
@ -95,6 +96,7 @@ class SimpleCrypto:
combined = f"{data}{secret_key}".encode('utf-8') combined = f"{data}{secret_key}".encode('utf-8')
return hashlib.sha256(combined).hexdigest() return hashlib.sha256(combined).hexdigest()
class AuthCache: class AuthCache:
"""授权信息缓存管理""" """授权信息缓存管理"""
@ -140,6 +142,407 @@ class AuthCache:
self.cache_data.clear() self.cache_data.clear()
self._save_cache() 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("<Return>", 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("<Return>", 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: class AuthValidator:
"""授权验证器主类""" """授权验证器主类"""
@ -149,18 +552,8 @@ class AuthValidator:
secret_key: str = "taiyi1224", secret_key: str = "taiyi1224",
cache_days: int = 7, cache_days: int = 7,
timeout: int = 3, timeout: int = 3,
gui_mode: bool = False): gui_mode: bool = True): # 默认启用GUI
""" """初始化验证器"""
初始化验证器
Args:
software_id: 软件ID在后台创建产品时生成
api_url: 后台API地址
secret_key: 验证密钥
cache_days: 离线缓存有效期
timeout: 网络请求超时时间
gui_mode: 是否使用图形界面输入卡密
"""
self.software_id = software_id self.software_id = software_id
self.api_url = api_url.rstrip('/') self.api_url = api_url.rstrip('/')
self.secret_key = secret_key self.secret_key = secret_key
@ -168,12 +561,10 @@ class AuthValidator:
self.timeout = timeout self.timeout = timeout
self.gui_mode = gui_mode self.gui_mode = gui_mode
# 初始化组件
self.machine_generator = MachineCodeGenerator() self.machine_generator = MachineCodeGenerator()
self.cache = AuthCache() self.cache = AuthCache()
self.machine_code = self._get_or_generate_machine_code() self.machine_code = self._get_or_generate_machine_code()
# 验证状态
self.failed_attempts = 0 self.failed_attempts = 0
self.last_attempt_time = None self.last_attempt_time = None
self.locked_until = None self.locked_until = None
@ -182,7 +573,6 @@ class AuthValidator:
"""获取或生成机器码""" """获取或生成机器码"""
cache_file = ".machine_code" cache_file = ".machine_code"
# 尝试从缓存加载
try: try:
if os.path.exists(cache_file): if os.path.exists(cache_file):
with open(cache_file, 'r') as f: with open(cache_file, 'r') as f:
@ -192,10 +582,8 @@ class AuthValidator:
except Exception: except Exception:
pass pass
# 生成新的机器码
machine_code = self.machine_generator.generate() machine_code = self.machine_generator.generate()
# 保存到缓存
try: try:
with open(cache_file, 'w') as f: with open(cache_file, 'w') as f:
f.write(machine_code) f.write(machine_code)
@ -204,29 +592,6 @@ class AuthValidator:
return machine_code 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: def _validate_license_format(self, license_key: str) -> bool:
"""验证卡密格式""" """验证卡密格式"""
if not license_key: if not license_key:
@ -234,77 +599,18 @@ class AuthValidator:
license_key = license_key.strip().replace(' ', '').replace('\t', '').upper() license_key = license_key.strip().replace(' ', '').replace('\t', '').upper()
# 检查长度16-32位
if len(license_key) < 16 or len(license_key) > 32: if len(license_key) < 16 or len(license_key) > 32:
return False return False
# 检查字符(只允许大写字母、数字和下划线)
import re import re
pattern = r'^[A-Z0-9_]+$' pattern = r'^[A-Z0-9_]+$'
return bool(re.match(pattern, license_key)) 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]]]: def _online_verify(self, license_key: str) -> Tuple[bool, str, Optional[Dict[str, Any]]]:
"""在线验证""" """在线验证"""
try: try:
url = f"{self.api_url}/auth/verify" url = f"{self.api_url}/auth/verify"
# 准备请求数据
# 使用与服务端一致的时间戳生成方式
timestamp = int(datetime.utcnow().timestamp()) timestamp = int(datetime.utcnow().timestamp())
data = { data = {
"software_id": self.software_id, "software_id": self.software_id,
@ -313,12 +619,10 @@ class AuthValidator:
"timestamp": timestamp "timestamp": timestamp
} }
# 生成验证签名
signature_data = f"{data['software_id']}{data['license_key']}{data['machine_code']}{data['timestamp']}" signature_data = f"{data['software_id']}{data['license_key']}{data['machine_code']}{data['timestamp']}"
signature = SimpleCrypto.generate_signature(signature_data, self.secret_key) signature = SimpleCrypto.generate_signature(signature_data, self.secret_key)
data["signature"] = signature data["signature"] = signature
# 发送请求
response = requests.post( response = requests.post(
url, url,
json=data, json=data,
@ -349,16 +653,13 @@ class AuthValidator:
if not auth_info: if not auth_info:
return False, "未找到有效的离线授权信息,请联网验证" return False, "未找到有效的离线授权信息,请联网验证"
# 检查缓存时间
cache_time = datetime.fromisoformat(auth_info.get('cache_time', '')) cache_time = datetime.fromisoformat(auth_info.get('cache_time', ''))
if datetime.utcnow() - cache_time > timedelta(days=self.cache_days): if datetime.utcnow() - cache_time > timedelta(days=self.cache_days):
return False, f"离线授权已过期(超过{self.cache_days}天),请联网验证" return False, f"离线授权已过期(超过{self.cache_days}天),请联网验证"
# 检查机器码匹配
if auth_info.get('machine_code') != self.machine_code: if auth_info.get('machine_code') != self.machine_code:
return False, "设备信息不匹配,请重新验证" return False, "设备信息不匹配,请重新验证"
# 检查有效期
expire_time = auth_info.get('expire_time') expire_time = auth_info.get('expire_time')
if expire_time: if expire_time:
expire_datetime = datetime.fromisoformat(expire_time) expire_datetime = datetime.fromisoformat(expire_time)
@ -373,52 +674,34 @@ class AuthValidator:
self.cache.set_auth_info(self.software_id, auth_info) self.cache.set_auth_info(self.software_id, auth_info)
def validate(self) -> bool: 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() offline_success, offline_message = self._offline_verify()
if offline_success: if offline_success:
return True return True
# 离线验证失败,尝试在线验证 # 离线验证失败显示GUI输入卡密
max_attempts = 3 # 最多尝试3次 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): success, message, auth_info = dialog.show()
# 输入卡密
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)
if success and auth_info: if success and auth_info:
# 验证成功,缓存授权信息
self._cache_auth_info(auth_info) self._cache_auth_info(auth_info)
# 检查是否需要更新
force_update = auth_info.get('force_update', False) force_update = auth_info.get('force_update', False)
download_url = auth_info.get('download_url') download_url = auth_info.get('download_url')
new_version = auth_info.get('new_version') new_version = auth_info.get('new_version')
if force_update and download_url: if force_update and download_url:
self._show_message("需要更新", f"发现新版本 {new_version}\n请下载更新后重新启动程序", True) ModernMessageBox.show_error(
# 尝试打开下载链接 "需要更新",
f"发现新版本 {new_version}\n请下载更新后重新启动程序"
)
try: try:
import webbrowser import webbrowser
webbrowser.open(download_url) webbrowser.open(download_url)
@ -426,84 +709,53 @@ class AuthValidator:
pass pass
return False 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 return True
else: else:
# 验证失败 return False
self.failed_attempts += 1 else:
self.last_attempt_time = datetime.utcnow() # 命令行模式(保留原有逻辑)
print("命令行模式暂未实现请使用gui_mode=True")
if self.failed_attempts >= 5: # 失败5次锁定 return False
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
def clear_cache(self): def clear_cache(self):
"""清除本地缓存""" """清除本地缓存"""
self.cache.clear_cache(self.software_id) self.cache.clear_cache(self.software_id)
# 删除机器码缓存文件
try: try:
if os.path.exists(".machine_code"): if os.path.exists(".machine_code"):
os.remove(".machine_code") os.remove(".machine_code")
except Exception: except Exception:
pass pass
# 便捷函数
# ===== 便捷函数 =====
def validate_license(software_id: str, **kwargs) -> bool: 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) validator = AuthValidator(software_id, **kwargs)
return validator.validate() return validator.validate()
def get_machine_code() -> str: def get_machine_code() -> str:
"""获取当前机器码""" """获取当前机器码"""
return MachineCodeGenerator.generate() 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("✗ 授权验证失败!")