You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python密码管理器.txt/.rtf导入导出功能故障排查求助

Python密码管理器导入导出问题排查与修复

问题现象

  • 导出操作弹出"Error Failed to export passwords!"错误,导出文件格式不符合预期
  • 导入功能无法正常加载密码数据

问题根源

  1. 加密密码序列化失败:Fernet加密后的密码是bytes类型,json.dump无法直接序列化该类型,这是导出失败的核心原因。
  2. 控件未初始化:update_password_list方法调用self.password_listbox,但类初始化时未创建该控件,导致存储密码时触发未定义错误,影响后续流程。
  3. 格式适配假实现:仅在文件对话框中设置了.rtf类型,但读写仍用JSON逻辑,未实际适配RTF格式。
  4. 异常处理不精准:导出时用宽泛的except Exception掩盖具体错误;导入失败时直接清空原有密码,数据安全性差。
  5. 密钥未持久化:每次启动生成新密钥,重启后无法解密之前存储的密码,导入数据也无法正常解密。

修复步骤

1. 序列化加密密码

导出时将bytes类型的加密密码转为base64字符串,导入时再转回bytes:

  • 加密后用encrypted_password.decode('utf-8')转为字符串存储
  • 导入后用encrypted_password.encode('utf-8')转回bytes用于解密

2. 补充列表框控件

在__init__中添加密码列表框,用于显示已存储的站点:

# 添加密码列表框
self.password_listbox = Listbox(self.root, width=40)
self.password_listbox.grid(row=9, column=0, columnspan=2, padx=10, pady=10)

3. 区分TXT/RTF格式处理

  • TXT格式:继续使用JSON序列化存储(转为字符串的加密密码)
  • RTF格式:生成包含站点和密码的富文本内容(示例简单实现,可根据需求扩展)

4. 优化异常处理

  • 捕获具体异常类型,输出错误详情
  • 导入失败时保留原有密码,避免数据丢失

5. 密钥持久化(可选)

将密钥保存到本地文件,重启程序后加载,确保加密解密一致性:

# 初始化时加载或生成密钥
def __init__(self):
    # ...其他代码
    self.key_file = "key.key"
    self.load_or_generate_key()

def load_or_generate_key(self):
    if os.path.exists(self.key_file):
        with open(self.key_file, "rb") as f:
            self.key = f.read()
    else:
        self.key = Fernet.generate_key()
        with open(self.key_file, "wb") as f:
            f.write(self.key)

修复后的完整代码

import json
import ssl
import random
import string
import os
from tkinter import *
from tkinter import messagebox, filedialog
from cryptography.fernet import Fernet

# Disable SSL certificate verification temporarily
ssl._create_default_https_context = ssl._create_unverified_context


class PasswordManager:
    def __init__(self):
        # Master password used to access the password manager
        self.master_password = "keem"

        # 加载或生成密钥
        self.key_file = "key.key"
        self.load_or_generate_key()

        # Dictionary to store the site names and encrypted passwords (字符串类型)
        self.passwords = {}

        # Initialize the main Tkinter window
        self.root = Tk()

        # Title for our window
        self.root.title("Password Manager")

        # Creating the master password entry box
        self.master_password_label = Label(self.root, text="Master Password:")
        self.master_password_label.grid(row=0, column=0)

        self.master_password_entry = Entry(self.root, show="*")
        self.master_password_entry.grid(row=0, column=1)

        # Creating the site name entry box
        self.site_label = Label(self.root, text="Site Name:")
        self.site_label.grid(row=1, column=0)

        self.site_entry = Entry(self.root)
        self.site_entry.grid(row=1, column=1)

        # Displaying the password requirements
        self.requirements = Label(self.root, text="Password Requirements: At least 8 characters, contains letters and numbers.")
        self.requirements.grid(row=2, column=0, columnspan=2)

        # Creating the password entry box
        self.password_label = Label(self.root, text="Password:")
        self.password_label.grid(row=3, column=0)

        self.password_entry = Entry(self.root, show="*")
        self.password_entry.grid(row=3, column=1)

        # Creating the buttons
        self.store_button = Button(self.root, text="Store Password", command=self.store_password)
        self.store_button.grid(row=4, column=1)

        self.generate_button = Button(self.root, text="Generate Password", command=self.generate_password)
        self.generate_button.grid(row=5, column=1)

        self.view_button = Button(self.root, text="View Passwords", command=self.view_passwords)
        self.view_button.grid(row=6, column=1)

        self.export_button = Button(self.root, text="Export Passwords", command=self.export_passwords)
        self.export_button.grid(row=7, column=1)

        self.import_button = Button(self.root, text="Import Passwords", command=self.import_passwords)
        self.import_button.grid(row=8, column=1)

        # 添加密码列表框
        self.password_listbox = Listbox(self.root, width=40)
        self.password_listbox.grid(row=9, column=0, columnspan=2, padx=10, pady=10)

        # Running the Tkinter event loop
        self.root.mainloop()

    def load_or_generate_key(self):
        if os.path.exists(self.key_file):
            try:
                with open(self.key_file, "rb") as f:
                    self.key = f.read()
            except Exception as e:
                messagebox.showerror("Error", f"Failed to load key: {str(e)}")
                self.key = Fernet.generate_key()
        else:
            self.key = Fernet.generate_key()
            try:
                with open(self.key_file, "wb") as f:
                    f.write(self.key)
            except Exception as e:
                messagebox.showerror("Error", f"Failed to save key: {str(e)}")

    def encrypt_password(self, password):
        f = Fernet(self.key)
        encrypted_password = f.encrypt(password.encode())
        # 转为字符串存储
        return encrypted_password.decode('utf-8')

    def decrypt_password(self, encrypted_password):
        f = Fernet(self.key)
        # 转回bytes解密
        decrypted_password = f.decrypt(encrypted_password.encode('utf-8')).decode()
        return decrypted_password

    @staticmethod
    def check_password_strength(password):
        has_number = any(char.isdigit() for char in password)
        has_letter = any(char.isalpha() for char in password)

        if len(password) < 8 or not has_number or not has_letter:
            return False
        else:
            return True

    def store_password(self):
        master = self.master_password_entry.get()
        site = self.site_entry.get()
        password = self.password_entry.get()

        if master != self.master_password:
            messagebox.showerror("Error", "Invalid master password!")
            return

        if not self.check_password_strength(password):
            messagebox.showerror("Error", "Password does not meet requirements.")
            return

        encrypted_password = self.encrypt_password(password)

        self.passwords[site] = encrypted_password

        self.update_password_list()
        self.master_password_entry.delete(0, 'end')
        self.site_entry.delete(0, 'end')
        self.password_entry.delete(0, 'end')

        messagebox.showinfo("Success", "Password stored successfully!")

    def generate_password(self):
        while True:
            letters = string.ascii_letters
            digits = string.digits
            password = ''.join(random.choice(letters + digits) for i in range(12))

            if self.check_password_strength(password):
                break

        self.password_entry.delete(0, 'end')
        self.password_entry.insert(0, password)

    def view_passwords(self):
        password_list = ""
        for site, encrypted_password in self.passwords.items():
            try:
                decrypted_password = self.decrypt_password(encrypted_password)
                password_list += f"Site: {site}\nPassword: {decrypted_password}\n\n"
            except Exception as e:
                password_list += f"Site: {site}\nError decrypting password: {str(e)}\n\n"

        if password_list:
            messagebox.showinfo("Stored Passwords", password_list)
        else:
            messagebox.showinfo("Stored Passwords", "No passwords stored.")

    def import_passwords(self):
        file_path = filedialog.askopenfilename(filetypes=(('Text Files', '*.txt'), ('Rich Text Files', '*.rtf')))
        if not file_path:
            return

        try:
            if file_path.endswith('.txt'):
                with open(file_path, "r") as file:
                    imported_data = json.load(file)
                    # 合并导入的密码,而非覆盖
                    self.passwords.update(imported_data)
            elif file_path.endswith('.rtf'):
                with open(file_path, "r") as file:
                    content = file.read()
                    # 简单解析RTF(仅提取站点和密码,实际RTF解析需更复杂逻辑)
                    lines = content.split('\n')
                    for line in lines:
                        if 'Site:' in line and 'Password:' in line:
                            site_part = line.split('Site:')[1].split('Password:')[0].strip()
                            password_part = line.split('Password:')[1].strip()
                            self.passwords[site_part] = password_part
            self.update_password_list()
            messagebox.showinfo("Success", "Passwords imported successfully!")
        except json.JSONDecodeError:
            messagebox.showerror("Error", "TXT file is not valid JSON!")
        except Exception as e:
            messagebox.showerror("Error", f"Failed to import passwords: {str(e)}")

    def export_passwords(self):
        file_path = filedialog.asksaveasfilename(
            filetypes=(('Text Files', '*.txt'), ('Rich Text Files', '*.rtf')),
            defaultextension=".txt"
        )
        if not file_path:
            return

        try:
            if file_path.endswith('.txt'):
                with open(file_path, "w") as file:
                    json.dump(self.passwords, file, indent=2)
            elif file_path.endswith('.rtf'):
                with open(file_path, "w") as file:
                    # 生成简单RTF内容
                    file.write("{\\rtf1\\ansi\\deff0\n")
                    file.write("\\b Password Manager Export\\b0\\par\n\n")
                    for site, encrypted_pwd in self.passwords.items():
                        file.write(f"Site: {site}\\par\n")
                        file.write(f"Encrypted Password: {encrypted_pwd}\\par\n\n")
                    file.write("}")
            messagebox.showinfo("Success", "Passwords exported successfully!")
        except Exception as e:
            messagebox.showerror("Error", f"Failed to export passwords: {str(e)}")

    def update_password_list(self):
        self.password_listbox.delete(0, END)
        for site in self.passwords:
            self.password_listbox.insert(END, site)

    def run(self):
        self.root.mainloop()


if __name__ == "__main__":
    password_manager = PasswordManager()
    password_manager.run()

内容的提问来源于stack exchange,提问作者munchman2000

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 09:04:52