吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 851|回复: 25
收起左侧

[Python 转载] 账号密码管理器修改版

[复制链接]
GwbStr 发表于 2025-3-18 10:21
本帖最后由 GwbStr 于 2025-3-20 16:08 编辑

结合大佬@Lovehacker技术  发布的 [Windows] 用DeepSeek编写的账号密码管理器

和大佬@lengrusong 的建议,使用deepseek,对上述代码进行了部分调整

image.png

import tkinter as tk
from tkinter import ttk, messagebox, filedialog, simpledialog
import json
import webbrowser
import os
import base64
import secrets
import string
import uuid
import shutil
import html
import bcrypt
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.backends import default_backend
from password_strength import PasswordPolicy

# 全局配置
CUSTOM_FONT = ("楷体", 10)
DEFAULT_DATA_DIR = os.path.join(os.path.expanduser("~"), "PasswordManager")
DATA_DIR = os.getenv("PASSWORD_MANAGER_DATA_DIR", DEFAULT_DATA_DIR)
ADMIN_FILE = os.path.join(DATA_DIR, "admin_password.enc")
DATA_FILE = os.path.join(DATA_DIR, "passwords.enc")
KEY_FILE = os.path.join(DATA_DIR, "secret.key")
COLORS = {
    "bg": "#F0F0F0",
    "button_bg": "#ADD8E6",
    "accent": "#4B8BBE",
    "text": "#333333"
}

# 确保数据目录存在
os.makedirs(DATA_DIR, exist_ok=True)

# 密码强度策略
PASSWORD_POLICY = PasswordPolicy.from_names(
    length=8,  # 最小长度
    uppercase=1,  # 至少一个大写字母
    numbers=1,  # 至少一个数字
    special=1,  # 至少一个特殊字符
)

def center_window(window):
    """窗口居中显示"""
    window.update_idletasks()
    width = window.winfo_width()
    height = window.winfo_height()
    x = (window.winfo_screenwidth() // 2) - (width // 2)
    y = (window.winfo_screenheight() // 2) - (height // 2)
    window.geometry(f'+{x}+{y}')

def hash_password(password):
    """使用 bcrypt 哈希密码"""
    salt = bcrypt.gensalt()
    return bcrypt.hashpw(password.encode(), salt)

def verify_password(password, hashed_password):
    """验证密码"""
    return bcrypt.checkpw(password.encode(), hashed_password)

def is_password_strong(password):
    """检查密码强度"""
    return not bool(PASSWORD_POLICY.test(password))

class SecureEncryptor:
    def __init__(self, password: str):
        self.password = password
        self.salt = None
        self.cipher = None
        self._initialize_encryption()

    def _initialize_encryption(self):
        if not os.path.exists(KEY_FILE):
            self._generate_new_key()

        with open(KEY_FILE, "rb") as f:
            self.salt = f.read(16)

        kdf = PBKDF2HMAC(
            algorithm=hashes.SHA256(),
            length=32,
            salt=self.salt,
            iterations=480000,
            backend=default_backend()
        )
        key = base64.urlsafe_b64encode(kdf.derive(self.password.encode()))
        self.cipher = Fernet(key)

    def _generate_new_key(self):
        self.salt = os.urandom(16)
        with open(KEY_FILE, "wb") as f:
            f.write(self.salt)

    def encrypt_data(self, data):
        return self.cipher.encrypt(json.dumps(data).encode())

    def decrypt_data(self, encrypted_data):
        try:
            decrypted = self.cipher.decrypt(encrypted_data)
            return json.loads(decrypted.decode())
        except Exception as e:
            messagebox.showerror("解密错误", f"数据解密失败: {str(e)}")
            return None

class LoginSystem:
    def __init__(self):
        self.login_window = tk.Tk()
        self.login_window.title("管理员登录")
        self.login_window.geometry("380x220")
        self.login_window.configure(bg=COLORS["bg"])
        center_window(self.login_window)  # 调用此函数将登录窗口居中
        self._init_styles()
        self.first_time_setup()
        self.create_login_ui()
        self.login_window.mainloop()

    def _init_styles(self):
        style = ttk.Style()
        style.theme_use("clam")
        style.configure("TButton",
                        background=COLORS["button_bg"],
                        foreground=COLORS["text"],
                        font=CUSTOM_FONT,
                        padding=8)
        style.map("TButton", background=[("active", COLORS["accent"])])
        style.configure("TLabel",
                        background=COLORS["bg"],
                        foreground=COLORS["text"])
        style.configure("TEntry",
                        fieldbackground="white",
                        foreground=COLORS["text"])

    def first_time_setup(self):
        """首次启动时设置管理员密码"""
        if not os.path.exists(ADMIN_FILE):
            self.show_set_password_dialog("首次启动,请设置管理员密码")

    def show_set_password_dialog(self, message):
        """显示设置密码对话框"""
        set_pass_win = tk.Toplevel(self.login_window)
        set_pass_win.title("设置管理员密码")
        set_pass_win.configure(bg=COLORS["bg"])
        center_window(set_pass_win)

        ttk.Label(set_pass_win, text=message).grid(row=0, column=0, columnspan=2, pady=5)
        ttk.Label(set_pass_win, text="新密码:").grid(row=1, column=0, padx=5, pady=5)
        new_pass_entry = ttk.Entry(set_pass_win, show="*")
        new_pass_entry.grid(row=1, column=1, padx=5, pady=5)

        ttk.Label(set_pass_win, text="确认密码:").grid(row=2, column=0, padx=5, pady=5)
        confirm_pass_entry = ttk.Entry(set_pass_win, show="*")
        confirm_pass_entry.grid(row=2, column=1, padx=5, pady=5)

        def save_password():
            new_pass = new_pass_entry.get()
            confirm_pass = confirm_pass_entry.get()
            if new_pass != confirm_pass:
                messagebox.showerror("错误", "两次输入的密码不一致!")
                return
            if not is_password_strong(new_pass):
                messagebox.showerror("错误", "密码强度不足!请确保密码包含大小写字母、数字和特殊字符。")
                return

            hashed_password = hash_password(new_pass)
            with open(ADMIN_FILE, "wb") as f:
                f.write(hashed_password)
            set_pass_win.destroy()
            messagebox.showinfo("成功", "管理员密码设置成功!")

        ttk.Button(set_pass_win, text="保存", command=save_password).grid(row=3, column=0, columnspan=2, pady=10)

    def create_login_ui(self):
        main_frame = ttk.Frame(self.login_window)
        main_frame.pack(padx=20, pady=20, fill=tk.BOTH, expand=True)

        # 锁图标
        lock_icon = ttk.Label(main_frame,
                              text="🔒",
                              font=("Arial", 32),
                              anchor="center")
        lock_icon.grid(row=0, column=2, columnspan=2, pady=10)

        # 输入区域
        input_frame = ttk.Frame(main_frame)
        input_frame.grid(row=1, column=2, columnspan=2, sticky="ew")

        ttk.Label(input_frame, text="管理员密码:").grid(row=0, column=2, pady=10, sticky="w")
        self.password_entry = ttk.Entry(input_frame, show="*")
        self.password_entry.grid(row=0, column=7, pady=10, sticky="ew")

        # 登录按钮
        btn_frame = ttk.Frame(main_frame)
        btn_frame.grid(row=2, column=2, columnspan=2, pady=15)
        ttk.Button(btn_frame,
                   text="🔑 登录",
                   command=self.verify_password).pack(ipadx=20)

        self.password_entry.bind("<Return>", lambda e: self.verify_password())

    def verify_password(self):
        input_password = self.password_entry.get().strip()

        if not input_password:
            messagebox.showwarning("错误", "请输入密码!")
            return

        try:
            with open(ADMIN_FILE, "rb") as f:
                hashed_password = f.read()

            if verify_password(input_password, hashed_password):
                self.login_window.destroy()
                encryptor = SecureEncryptor(input_password)
                PasswordManagerGUI(encryptor)
            else:
                messagebox.showerror("错误", "密码错误!")
        except Exception as e:
            messagebox.showerror("错误", f"登录失败: {str(e)}")

class PasswordManagerGUI:
    def __init__(self, encryptor):
        self.root = tk.Tk()
        self.root.title("密码管理系统")
        self.root.configure(bg=COLORS["bg"])
        center_window(self.root)
        self._init_styles()
        self.encryptor = encryptor
        self.passwords = self.load_data()
        self.clipboard_timeout = 15  # 默认剪贴板清除时间
        self.settings_window = None  # 新增属性,用于记录系统设置子窗口
        self._setup_ui()
        self.root.mainloop()

    def _init_styles(self):
        style = ttk.Style()
        style.theme_use("clam")
        style.configure(".",
                        background=COLORS["bg"],
                        foreground=COLORS["text"],
                        font=CUSTOM_FONT)
        style.configure("TButton",
                        background=COLORS["button_bg"],
                        foreground=COLORS["text"],
                        padding=8,
                        borderwidth=0)
        style.map("TButton",
                  background=[("active", COLORS["accent"])])
        style.configure("Treeview",
                        rowheight=25,
                        fieldbackground="white",
                        foreground=COLORS["text"])
        style.configure("Treeview.Heading",
                        background=COLORS["accent"],
                        foreground="white",
                        font=("楷体", 10, "bold"))
        style.configure("TLabelframe",
                        background=COLORS["bg"],
                        foreground=COLORS["text"])
        style.configure("TLabelframe.Label",
                        background=COLORS["bg"],
                        foreground=COLORS["accent"])

    def _setup_ui(self):
        self._create_toolbar()
        self._create_input_area()
        self._create_buttons()
        self._create_treeview()
        self._create_context_menu()
        self.update_tree()

    def _create_toolbar(self):
        toolbar = ttk.Frame(self.root)
        toolbar.pack(side=tk.TOP, fill=tk.X, padx=5, pady=2)
        ttk.Button(toolbar,
                   text="⚙️ 系统设置",
                   command=self.show_settings).pack(side=tk.RIGHT, padx=5)

    def _create_input_area(self):
        input_frame = ttk.LabelFrame(self.root, text="📝; 密码信息(*为必填项)")
        input_frame.pack(padx=10, pady=5, fill=tk.BOTH, expand=True)

        fields = [
            ("*名称", True), ("*网址", True),
            ("*用户名", True), ("*密码", True),
            ("绑定手机号", False), ("绑定邮箱", False)
        ]

        self.entries = {}
        for row, (field, required) in enumerate(fields):
            lbl_text = field.replace("*", "")
            ttk.Label(input_frame, text=lbl_text).grid(row=row, column=0, padx=5, pady=2, sticky="e")

            if required:
                ttk.Label(input_frame, text="*", foreground="red").grid(row=row, column=1, sticky="w")

            entry = ttk.Entry(input_frame)
            entry.grid(row=row, column=2, padx=5, pady=2, sticky="ew")
            self.entries[lbl_text] = entry

            if not required:
                ttk.Label(input_frame, text="(选填)", foreground="gray").grid(row=row, column=3, padx=5)

        # 密码生成器
        gen_frame = ttk.Frame(input_frame)
        gen_frame.grid(row=3, column=4, rowspan=2, padx=10)
        ttk.Button(gen_frame,
                   text="🎮 生成密码",
                   command=self.generate_password).pack(pady=2)
        self.generated_pw_entry = ttk.Entry(gen_frame, width=20)
        self.generated_pw_entry.pack(pady=2)
        ttk.Button(gen_frame,
                   text="➡️ 应用密码",
                   command=self.apply_generated_password).pack(pady=2)

    def _create_buttons(self):
        btn_frame = ttk.Frame(self.root)
        btn_frame.pack(pady=10, fill=tk.X, padx=10)

        buttons = [
            ("➕ 新增记录", self.add_password),
            ("🗑️ 删除记录", self.delete_password),
            ("✏️ 修改记录", self.update_password),
            ("🔍 搜索记录", self.search_password),
            ("🌐 打开网站", self.open_url)
        ]

        for text, cmd in buttons:
            ttk.Button(btn_frame,
                       text=text,
                       command=cmd).pack(side=tk.LEFT, padx=3, ipadx=5)

    def _create_treeview(self):
        tree_frame = ttk.Frame(self.root)
        tree_frame.pack(padx=10, pady=5, fill=tk.BOTH, expand=True)

        # 水平滚动条
        tree_scroll_x = ttk.Scrollbar(tree_frame, orient="horizontal")
        self.tree = ttk.Treeview(
            tree_frame,
            columns=("name", "username", "url"),
            show="headings",
            selectmode="browse",
            xscrollcommand=tree_scroll_x.set
        )
        tree_scroll_x.config(command=self.tree.xview)
        tree_scroll_x.pack(side=tk.BOTTOM, fill=tk.X)

        columns = [
            ("name", "名称", 250),
            ("username", "用户名", 180),
            ("url", "网址", 400)
        ]

        for col_id, text, width in columns:
            self.tree.heading(col_id, text=text, anchor="center")
            self.tree.column(col_id, width=width, anchor="w", stretch=True)

        self.tree.pack(fill=tk.BOTH, expand=True)
        self.tree.bind("<Double-1>", self.show_details)

    def _create_context_menu(self):
        self.context_menu = tk.Menu(self.root, tearoff=0)
        self.context_menu.add_command(
            label="📋 复制用户名",
            command=lambda: self.copy_selected_field("用户名"))
        self.context_menu.add_command(
            label="📋 复制密码",
            command=lambda: self.copy_selected_field("密码"))
        self.tree.bind("<Button-3>", self.show_context_menu)

    def show_context_menu(self, event):
        item = self.tree.identify_row(event.y)
        if item:
            self.tree.selection_set(item)
            self.context_menu.tk_popup(event.x_root, event.y_root)

    def copy_selected_field(self, field_name):
        selected = self.tree.selection()
        if not selected:
            return

        index = self.tree.index(selected[0])
        value = self.passwords[index].get(field_name, "")
        if value:
            self.copy_to_clipboard(value)

    def load_data(self):
        try:
            if not os.path.exists(DATA_FILE):
                return []

            with open(DATA_FILE, "rb") as f:
                encrypted_data = f.read()
                data = self.encryptor.decrypt_data(encrypted_data)
                if data is None:
                    messagebox.showerror("错误", "数据解密失败,请检查密码是否正确")
                    return []
                return data if isinstance(data, list) else []
        except Exception as e:
            messagebox.showerror("错误", f"数据加载失败: {str(e)}")
            return []

    def save_data(self):
        try:
            with open(DATA_FILE, "wb") as f:
                encrypted_data = self.encryptor.encrypt_data(self.passwords)
                f.write(encrypted_data)
        except Exception as e:
            messagebox.showerror("错误", f"数据保存失败: {str(e)}")
            raise

    def generate_password(self):
        password = generate_strong_password()
        self.generated_pw_entry.delete(0, tk.END)
        self.generated_pw_entry.insert(0, password)

    def apply_generated_password(self):
        password = self.generated_pw_entry.get()
        if password:
            self.entries["密码"].delete(0, tk.END)
            self.entries["密码"].insert(0, password)

    def get_input_data(self):
        return {key: entry.get() for key, entry in self.entries.items()}

    def clear_inputs(self):
        for entry in self.entries.values():
            entry.delete(0, tk.END)
        self.generated_pw_entry.delete(0, tk.END)

    def add_password(self):
        data = self.get_input_data()
        required_fields = ["名称", "网址", "用户名", "密码"]

        for field in required_fields:
            if not data[field].strip():
                messagebox.showwarning("错误", f"{field}不能为空!")
                return

        self.passwords.append(data)
        try:
            self.save_data()
        except:
            self.passwords.pop()
            return
        self.update_tree()
        self.clear_inputs()
        messagebox.showinfo("成功", "✅ 数据添加成功!")

    def delete_password(self):
        selected = self.tree.selection()
        if not selected:
            messagebox.showwarning("错误", "请先选择要删除的数据!")
            return

        index = self.tree.index(selected[0])
        del self.passwords[index]
        self.save_data()
        self.update_tree()

    def update_password(self):
        selected = self.tree.selection()
        if not selected:
            messagebox.showwarning("错误", "请先选择要修改的数据!")
            return

        index = self.tree.index(selected[0])
        original_data = self.passwords[index]

        edit_win = tk.Toplevel(self.root)
        edit_win.title("✏️ 修改数据")
        edit_win.configure(bg=COLORS["bg"])
        center_window(edit_win)
        edit_win.grab_set()  # 获取焦点,使主窗口不可用

        entries = {}
        for row, (key, value) in enumerate(original_data.items()):
            ttk.Label(edit_win, text=key).grid(row=row, column=0, padx=5, pady=2)
            entry = ttk.Entry(edit_win)
            entry.insert(0, value)
            entry.grid(row=row, column=1, padx=5, pady=2)
            entries[key] = entry

        def save_changes():
            new_data = {key: entry.get() for key, entry in entries.items()}
            required_fields = ["名称", "网址", "用户名", "密码"]
            for field in required_fields:
                if not new_data[field].strip():
                    messagebox.showwarning("错误", f"{field}不能为空!")
                    return
            self.passwords[index] = new_data
            self.save_data()
            self.update_tree()
            edit_win.destroy()
            edit_win.grab_release()  # 释放焦点,使主窗口可用
            messagebox.showinfo("成功", "✅ 修改已保存!")

        ttk.Button(edit_win,
                   text="💾 保存修改",
                   command=save_changes).grid(
            row=len(original_data),
            column=0,
            columnspan=2,
            pady=10)

    def show_settings(self):
        if self.settings_window and self.settings_window.winfo_exists():
            self.settings_window.lift()  # 如果子窗口已存在,将其提升到最前
            return

        self.settings_window = tk.Toplevel(self.root)
        self.settings_window.title("⚙️ 系统设置")
        self.settings_window.geometry("300x220")
        self.settings_window.configure(bg=COLORS["bg"])
        center_window(self.settings_window)
        self.settings_window.grab_set()  # 获取焦点,使主窗口不可用

        buttons = [
            ("🔑 修改密码", self.change_password),
            ("💾 数据备份", self.export_backup),
            ("🔀 数据恢复", self.import_backup),
            ("ℹ️ 关于程序", self.show_about)
        ]

        def close_settings():
            self.settings_window.destroy()
            self.settings_window = None  # 子窗口关闭后,将记录设为 None

        for text, cmd in buttons:
            ttk.Button(self.settings_window,
                       text=text,
                       command=cmd).pack(pady=5, fill=tk.X, padx=20)

        self.settings_window.protocol("WM_DELETE_WINDOW", close_settings)

    def export_backup(self):
        backup_file = filedialog.asksaveasfilename(
            defaultextension=".bak",
            filetypes=[("备份文件", "*.bak")],
            initialdir=DATA_DIR
        )
        if backup_file:
            backup_password = simpledialog.askstring("备份密码", "请输入备份密码:", show="*")
            if not backup_password:
                return

            try:
                encryptor = SecureEncryptor(backup_password)
                with open(DATA_FILE, "rb") as f:
                    data = f.read()
                encrypted_data = encryptor.encrypt_data(data)
                with open(backup_file, "wb") as f:
                    f.write(encrypted_data)
                messagebox.showinfo("成功", f"📂 备份已保存到:{backup_file}")
            except Exception as e:
                messagebox.showerror("错误", f"备份失败: {str(e)}")

    def import_backup(self):
        backup_file = filedialog.askopenfilename(
            filetypes=[("备份文件", "*.bak")],
            initialdir=DATA_DIR
        )
        if backup_file:
            backup_password = simpledialog.askstring("备份密码", "请输入备份密码:", show="*")
            if not backup_password:
                return

            try:
                encryptor = SecureEncryptor(backup_password)
                with open(backup_file, "rb") as f:
                    encrypted_data = f.read()
                decrypted_data = encryptor.decrypt_data(encrypted_data)
                if decrypted_data is None:
                    messagebox.showerror("错误", "备份文件解密失败!")
                    return

                with open(DATA_FILE, "wb") as f:
                    f.write(self.encryptor.encrypt_data(decrypted_data))
                self.passwords = self.load_data()
                self.update_tree()
                messagebox.showinfo("成功", "🌀 数据恢复完成!")
            except Exception as e:
                messagebox.showerror("错误", f"恢复失败: {str(e)}")

    def show_about(self):
        about_info = """🔒 密码管理系统 v1.0

功能特性:
- AES-256 加密存储
- PBKDF2 密钥派生
- 智能表单自动填充
- 剪贴板自动清除
- 数据备份与恢复

安全提示:
请定期修改管理员密码"""
        messagebox.showinfo("关于程序", about_info)

    def change_password(self):
        pass_win = tk.Toplevel(self.root)
        pass_win.title("🔑 修改密码")
        pass_win.configure(bg=COLORS["bg"])
        center_window(pass_win)

        ttk.Label(pass_win, text="当前密码:").grid(row=0, column=0, padx=5, pady=5)
        current_pass = ttk.Entry(pass_win, show="*")
        current_pass.grid(row=0, column=1, padx=5, pady=5)

        ttk.Label(pass_win, text="新密码:").grid(row=1, column=0, padx=5, pady=5)
        new_pass = ttk.Entry(pass_win, show="*")
        new_pass.grid(row=1, column=1, padx=5, pady=5)

        ttk.Label(pass_win, text="确认密码:").grid(row=2, column=0, padx=5, pady=5)
        confirm_pass = ttk.Entry(pass_win, show="*")
        confirm_pass.grid(row=2, column=1, padx=5, pady=5)

        def save_new_password():
            current = current_pass.get()
            new = new_pass.get()
            confirm = confirm_pass.get()

            if new != confirm:
                messagebox.showerror("错误", "⚠️; 两次输入的密码不一致!")
                return

            if not is_password_strong(new):
                messagebox.showerror("错误", "⚠️; 密码强度不足!请确保密码包含大小写字母、数字和特殊字符。")
                return

            try:
                with open(ADMIN_FILE, "rb") as f:
                    hashed_password = f.read()

                if not verify_password(current, hashed_password):
                    messagebox.showerror("错误", "🔒 当前密码验证失败!")
                    return

                new_hashed_password = hash_password(new)
                with open(ADMIN_FILE, "wb") as f:
                    f.write(new_hashed_password)

                new_encryptor = SecureEncryptor(new)
                new_encrypted_data = new_encryptor.encrypt_data(self.passwords)
                with open(DATA_FILE, "wb") as f:
                    f.write(new_encrypted_data)

                messagebox.showinfo("成功", "✅ 密码修改成功!")
                pass_win.destroy()
            except Exception as e:
                messagebox.showerror("错误", f"修改失败: {str(e)}")

        ttk.Button(pass_win,
                   text="🔑; 保存新密码",
                   command=save_new_password).grid(
            row=3,
            columnspan=2,
            pady=15)

    def search_password(self):
        search_win = tk.Toplevel(self.root)
        search_win.title("🔍  搜索记录")
        search_win.configure(bg=COLORS["bg"])
        center_window(search_win)
        search_win.transient(self.root)
        search_win.grab_set()  # 获取焦点,使主窗口不可用

        ttk.Label(search_win, text="搜索关键字:").grid(row=0, column=0, padx=5, pady=5)
        keyword_entry = ttk.Entry(search_win)
        keyword_entry.grid(row=0, column=1, padx=5, pady=5)

        ttk.Label(search_win, text="搜索字段:").grid(row=1, column=0, padx=5, pady=5)
        field_combobox = ttk.Combobox(
            search_win,
            values=["名称", "网址", "用户名", "密码", "绑定手机号", "绑定邮箱"],
            state="readonly"
        )
        field_combobox.current(0)
        field_combobox.grid(row=1, column=1, padx=5, pady=5)

        def perform_search():
            keyword = keyword_entry.get().strip().lower()
            field = field_combobox.get()

            if not keyword:
                messagebox.showwarning("错误", " ⚠️ 请输入搜索关键字!")
                return

            filtered = []
            for item in self.passwords:
                value = str(item.get(field, "")).lower()
                if keyword in value:
                    filtered.append(item)

            # 在主窗口上层显示结果
            result_win = tk.Toplevel(self.root)
            result_win.title("🔍 搜索结果")
            result_win.configure(bg=COLORS["bg"])
            center_window(result_win)
            result_win.lift(self.root)  # 确保显示在父窗口上层
            result_win.grab_set()  # 获取焦点,使主窗口不可用

            # 创建结果表格
            tree_frame = ttk.Frame(result_win)
            tree_frame.pack(padx=10, pady=10, fill=tk.BOTH, expand=True)

            # 添加滚动条
            tree_scroll = ttk.Scrollbar(tree_frame)
            tree = ttk.Treeview(
                tree_frame,
                columns=("name", "username", "url"),
                show="headings",
                yscrollcommand=tree_scroll.set
            )
            tree_scroll.config(command=tree.yview)
            tree_scroll.pack(side=tk.RIGHT, fill=tk.Y)

            columns = [
                ("name", "名称", 200),
                ("username", "用户名", 150),
                ("url", "网址", 300)
            ]

            for col_id, text, width in columns:
                tree.heading(col_id, text=text)
                tree.column(col_id, width=width, anchor="w")

            if filtered:
                for item in filtered:
                    tree.insert("", "end", values=(
                        item["名称"],
                        item["用户名"],
                        item["网址"]
                    ))
                tree.bind("<Double-1>", lambda e: self.show_details(e, data=filtered))
            else:
                tree.insert("", "end", values=("未找到匹配记录",))

            tree.pack(fill=tk.BOTH, expand=True)
            search_win.destroy()
            search_win.grab_release()  # 释放焦点,使主窗口可用
            result_win.destroy()
            result_win.grab_release()  # 释放焦点,使主窗口可用
            self.root.after(100, lambda: center_window(self.root))

        ttk.Button(search_win,
                   text="🔍 开始搜索",
                   command=perform_search).grid(
            row=2,
            columnspan=2,
            pady=15)

    def open_url(self):
        selected = self.tree.selection()
        if not selected:
            return

        index = self.tree.index(selected[0])
        entry = self.passwords[index]
        url = entry.get("网址", "")

        if url:
            try:
                autofill_html = self._generate_autofill_page(entry)
                # 使用 uuid 生成唯一的文件名
                temp_file_name = f"temp_autofill_{uuid.uuid4()}.html"
                temp_file = os.path.join(DATA_DIR, temp_file_name)

                with open(temp_file, "w", encoding="utf-8") as f:
                    f.write(autofill_html)

                # 先打开目标网址
                webbrowser.open(url)
                # 可以选择保留临时文件一段时间,用于自动填充
                self.root.after(5000, lambda: self._cleanup_temp_file(temp_file))
            except Exception as e:
                messagebox.showerror("错误", f"🌐; 自动填充失败: {str(e)}")

    def _cleanup_temp_file(self, filename):
        try:
            if os.path.exists(filename):
                os.remove(filename)
        except Exception as e:
            print(f"清理临时文件失败: {str(e)}")

    def _generate_autofill_page(self, entry):
        escaped_password = html.escape(entry['密码'])
        return f"""
        <!DOCTYPE html>
        <html>
        <head>
            <meta charset="UTF-8">
            <title>自动填充</title>
            <script>
                function autoFill() {{
                    try {{
                        const userKeywords = ['user', 'login', 'account', 'email', 'username', 'name'];
                        const passKeywords = ['pass', 'password', 'pwd', 'secret'];
                        let usernameFilled = false;
                        let passwordFilled = false;

                        document.querySelectorAll('input').forEach(input => {{
                            const lowerType = (input.type || '').toLowerCase();
                            const lowerName = (input.name || '').toLowerCase();
                            const lowerId = (input.id || '').toLowerCase();
                            const lowerPlaceholder = (input.placeholder || '').toLowerCase();

                            // 填充用户名
                            if (!usernameFilled) {{
                                if (lowerType === 'text' || lowerType === 'email') {{
                                    if (userKeywords.some(kw => lowerName.includes(kw)) ||
                                        userKeywords.some(kw => lowerId.includes(kw)) ||
                                        lowerPlaceholder.includes('用户名') ||
                                        lowerPlaceholder.includes('账号')) {{
                                        input.value = '{entry['用户名']}';
                                        usernameFilled = true;
                                    }}
                                }}
                            }}

                            // 填充密码
                            if (!passwordFilled) {{
                                if (lowerType === 'password') {{
                                    if (passKeywords.some(kw => lowerName.includes(kw)) ||
                                        passKeywords.some(kw => lowerId.includes(kw)) ||
                                        lowerPlaceholder.includes('密码')) {{
                                        input.value = '{escaped_password}';
                                        passwordFilled = true;
                                    }}
                                }}
                            }}
                        }});

                        // 自动提交表单
                        if (usernameFilled && passwordFilled) {{
                            document.querySelector('form')?.submit();
                        }}
                    }} catch (e) {{
                        console.error('自动填充错误:', e);
                    }}
                }}

                // 多种触发方式
                if (document.readyState === 'complete') {{
                    autoFill();
                }} else {{
                    window.addEventListener('load', autoFill);
                    document.addEventListener('DOMContentLoaded', autoFill);
                }}
            </script>
        </head>
        <body>
            <noscript>
                <p>请启用JavaScript以获得自动填充功能</p>
                <a href="{entry['网址']}">手动访问网站</a>
            </noscript>
            <p>如果页面没有自动跳转,<a href="{entry['网址']}">请点击这里</a></p>
        </body>
        </html>
        """

    def update_tree(self, data=None):
        self.tree.delete(*self.tree.get_children())
        data = data or self.passwords
        for item in data:
            self.tree.insert("", "end", values=(
                item["名称"],
                item["用户名"],
                item["网址"]
            ))

    def show_details(self, event, data=None):
        tree = event.widget
        selected = tree.selection()
        if not selected:
            return

        index = tree.index(selected[0])
        details = (data or self.passwords)[index]

        detail_win = tk.Toplevel(self.root)
        detail_win.title("🔍 详细信息")
        detail_win.configure(bg=COLORS["bg"])
        center_window(detail_win)

        for row, (key, value) in enumerate(details.items()):
            ttk.Label(detail_win, text=f"{key}:").grid(row=row, column=0, padx=5, pady=2, sticky="e")
            entry = ttk.Entry(detail_win, width=40)
            entry.insert(0, value)
            entry.config(state="readonly")
            entry.grid(row=row, column=1, padx=5, pady=2, sticky="w")
            ttk.Button(detail_win,
                      text="📋 复制",
                      command=lambda v=value, win=detail_win: self.copy_with_focus(v, win)
                      ).grid(row=row, column=2, padx=5)

    def copy_with_focus(self, text, parent_window):
        self.copy_to_clipboard(text)
        parent_window.lift()

    def copy_to_clipboard(self, text):
        self.root.clipboard_clear()
        self.root.clipboard_append(text)
        msg = messagebox.showinfo("复制成功", "📋 内容已复制到剪贴板,15秒后自动清除")
        self.root.after(self.clipboard_timeout * 1000, self.clear_clipboard)

    def clear_clipboard(self):
        self.root.clipboard_clear()

def generate_strong_password(length=16):
    """生成符合NIST标准的强密码"""
    if length < 12:
        length = 12

    while True:
        uppercase = secrets.choice(string.ascii_uppercase)
        lowercase = secrets.choice(string.ascii_lowercase)
        digit = secrets.choice(string.digits)
        special = secrets.choice("!@#$%^&*~-+_=")

        remaining = length - 4
        chars = string.ascii_letters + string.digits + "!@#$%^&*~-+_="
        others = [secrets.choice(chars) for _ in range(remaining)]

        password_chars = [uppercase, lowercase, digit, special] + others
        secrets.SystemRandom().shuffle(password_chars)
        password = ''.join(password_chars)

        # 验证密码强度
        if (any(c.islower() for c in password) and
            any(c.isupper() for c in password) and
            any(c.isdigit() for c in password) and
            sum(c in "!@#$%^&*~-+_=" for c in password) >= 2 and
            not any(password[i] == password[i+1] for i in range(len(password)-1))):
            return password

if __name__ == "__main__":
    LoginSystem()


目前大佬@lengrusong 的建议基本已经实现,但是代码运行还是存在部分问题,个人目前没有相关的办法解决。如果有大佬知道如何解决,欢迎告诉我解决的方法,这里先谢过各位大佬了:

1.运行的时候会报下边的错误,按照deepseek上边说的方式设置了忽略,但是不生效,目前相关代码已经删除

image.png

2.这个登录ui太丑了,优化了很多次,但是依旧很丑

image.png

3.系统设置的子菜单,里面的所有按钮都可以打开多个,而且修改密码逻辑存在问题,点击以后只能点击保存新密码才能关闭,右上角关闭没有效果

image.png

免费评分

参与人数 3吾爱币 +3 热心值 +2 收起 理由
qianyuqianyue + 1 + 1 谢谢@Thanks!
shandian3523 + 1 鼓励转贴优秀软件安全工具和文档!
YZM23333 + 1 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| GwbStr 发表于 2025-3-20 08:47
打包了一个exe文件,有需要的盆友可以自行下载

通过网盘分享的文件:test.exe
链接: https://pan.baidu.com/s/1hZFiUh89VwopYZTUbQBsiQ?pwd=52pj 提取码: 52pj
--来自百度网盘超级会员v1的分享
 楼主| GwbStr 发表于 2025-3-24 08:51
JeremyHou 发表于 2025-3-23 04:50
试了两个常用网站,不太理解,我把密码存在里面并加密应用了, 然后点击打开网址,还是要我输入密码,那我 ...

其实就是一个本地的密码“保险柜”,因为我有很多密码需要保存,有时候明文保存存在一定的风险,所以就试了一下这种方式。如果你有这个密码要存储到本地,然后在各个浏览器都可以使用的话,代码在第一页,你可以在这个上边进行一定的开发
lingjin3 发表于 2025-3-18 12:18
木水车 发表于 2025-3-18 12:48
感谢分享,有没有成品?
qvdzf 发表于 2025-3-18 13:03
怎么弄成可执行文件
8204118 发表于 2025-3-18 13:20
    import bcrypt
ModuleNotFoundError: No module named 'bcrypt'
zengyi2020 发表于 2025-3-18 13:51
确实需要成品
WLHSDXN 发表于 2025-3-19 10:49
期待成品!
 楼主| GwbStr 发表于 2025-3-20 08:48
木水车 发表于 2025-3-18 12:48
感谢分享,有没有成品?

刚打包了,你还有需要的话可以看看
 楼主| GwbStr 发表于 2025-3-20 08:49

刚打包了,你还有需要的话可以看看
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-4-15 18:47

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表