Python密码管理器.txt/.rtf导入导出功能故障排查求助
Python密码管理器导入导出问题排查与修复
问题现象
- 导出操作弹出"Error Failed to export passwords!"错误,导出文件格式不符合预期
- 导入功能无法正常加载密码数据
问题根源
- 加密密码序列化失败:Fernet加密后的密码是
bytes类型,json.dump无法直接序列化该类型,这是导出失败的核心原因。 - 控件未初始化:
update_password_list方法调用self.password_listbox,但类初始化时未创建该控件,导致存储密码时触发未定义错误,影响后续流程。 - 格式适配假实现:仅在文件对话框中设置了
.rtf类型,但读写仍用JSON逻辑,未实际适配RTF格式。 - 异常处理不精准:导出时用宽泛的
except Exception掩盖具体错误;导入失败时直接清空原有密码,数据安全性差。 - 密钥未持久化:每次启动生成新密钥,重启后无法解密之前存储的密码,导入数据也无法正常解密。
修复步骤
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
相关产品推荐
相关产品推荐

