更新验证页面
This commit is contained in:
parent
1666e9cfcb
commit
262e799354
@ -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:
|
|
||||||
# 验证失败
|
|
||||||
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:
|
else:
|
||||||
return False
|
return False
|
||||||
|
else:
|
||||||
|
# 命令行模式(保留原有逻辑)
|
||||||
|
print("命令行模式暂未实现,请使用gui_mode=True")
|
||||||
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("✗ 授权验证失败!")
|
||||||
Loading…
Reference in New Issue
Block a user