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

Tkinter应用用户凭据加密存储与解密调用解决方案咨询

解决Fernet重复加密旧数据的问题

核心问题分析

重复加密旧数据的本质是读取已加密的JSON数据时未解密,直接将加密字符串当作明文再次加密,或是密钥未持久化导致旧数据无法解密(常被误判为重复加密)。

解决方案

1. 密钥持久化管理

必须固定密钥,不能每次启动应用生成新密钥,否则旧加密数据将永久无法解密。

from cryptography.fernet import Fernet
import json
import os

# 密钥存储路径,建议放在非公开目录
KEY_PATH = "cred_secret.key"

def get_fernet():
    # 加载已有密钥,不存在则生成并保存
    if os.path.exists(KEY_PATH):
        with open(KEY_PATH, "rb") as f:
            key = f.read()
    else:
        key = Fernet.generate_key()
        with open(KEY_PATH, "wb") as f:
            f.write(key)
    return Fernet(key)

fernet = get_fernet()

2. 数据读写流程规范

严格遵循读取时解密所有加密字段,写入时仅加密明文字段的逻辑:

读取凭据(解密)

def load_credentials():
    CRED_FILE = "credentials.json"
    if not os.path.exists(CRED_FILE):
        return []
    
    with open(CRED_FILE, "r") as f:
        encrypted_list = json.load(f)
    
    # 解密每个凭据的用户名和密码
    decrypted_list = []
    for item in encrypted_list:
        decrypted_list.append({
            "id": item["id"],
            "site": item["site"],
            "username": fernet.decrypt(item["username"].encode()).decode(),
            "password": fernet.decrypt(item["password"].encode()).decode()
        })
    return decrypted_list

保存凭据(加密)

def save_credentials(cred_list):
    CRED_FILE = "credentials.json"
    encrypted_list = []
    
    for cred in cred_list:
        encrypted_list.append({
            "id": cred["id"],
            "site": cred["site"],
            "username": fernet.encrypt(cred["username"].encode()).decode(),
            "password": fernet.encrypt(cred["password"].encode()).decode()
        })
    
    with open(CRED_FILE, "w") as f:
        json.dump(encrypted_list, f, indent=2)

3. 新增/更新凭据逻辑

基于明文数据操作,避免触碰已加密内容:

新增凭据

def add_new_credential(new_cred):
    # new_cred是明文字典:{"id": 1, "site": "example.com", "username": "user1", "password": "pass123"}
    all_creds = load_credentials()
    all_creds.append(new_cred)
    save_credentials(all_creds)

更新凭据

def update_credential(target_id, updated_cred):
    # updated_cred是更新后的明文数据
    all_creds = load_credentials()
    for idx, cred in enumerate(all_creds):
        if cred["id"] == target_id:
            all_creds[idx] = updated_cred
            break
    save_credentials(all_creds)

4. Treeview选择后解密发送

def on_tree_select(event):
    selected_item = treeview.selection()[0]
    cred_id = treeview.item(selected_item, "values")[0]  # 假设ID是Treeview第一列
    
    all_creds = load_credentials()
    target_cred = next(c for c in all_creds if c["id"] == cred_id)
    
    # 直接使用明文发送到网站
    send_to_website(target_cred["username"], target_cred["password"])

# 绑定Treeview选择事件
treeview.bind("<<TreeviewSelect>>", on_tree_select)

常见错误规避

  • 禁止读取JSON后直接对加密字段再次加密,必须先解密得到明文
  • 密钥不能随应用重启生成,必须持久化存储
  • 不要混合存储明文和加密数据,统一在写入前加密、读取后解密

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 01:10:28