""" 加密服务 提供敏感数据的加密和解密功能 """ from cryptography.fernet import Fernet from app.core.config import settings import base64 import hashlib import logging logger = logging.getLogger(__name__) class EncryptionService: """加密服务类""" _fernet: Fernet = None @classmethod def _get_fernet(cls) -> Fernet: """获取Fernet加密实例(单例模式)""" if cls._fernet is None: # 使用SECRET_KEY生成Fernet密钥 # Fernet需要32字节的密钥,我们使用SHA256哈希SECRET_KEY key = hashlib.sha256(settings.SECRET_KEY.encode()).digest() # Fernet需要base64编码的32字节密钥 fernet_key = base64.urlsafe_b64encode(key) cls._fernet = Fernet(fernet_key) return cls._fernet @classmethod def encrypt(cls, plaintext: str) -> str: """ 加密明文 Args: plaintext: 要加密的明文 Returns: 加密后的密文(base64编码) """ if not plaintext: return "" try: fernet = cls._get_fernet() encrypted = fernet.encrypt(plaintext.encode('utf-8')) return encrypted.decode('utf-8') except Exception as e: logger.error(f"加密失败: {e}") raise ValueError(f"加密失败: {str(e)}") @classmethod def decrypt(cls, ciphertext: str) -> str: """ 解密密文 Args: ciphertext: 要解密的密文(base64编码) Returns: 解密后的明文 """ if not ciphertext: return "" try: fernet = cls._get_fernet() decrypted = fernet.decrypt(ciphertext.encode('utf-8')) return decrypted.decode('utf-8') except Exception as e: logger.error(f"解密失败: {e}") # 如果解密失败,可能是旧数据未加密,直接返回原值 # 或者抛出异常,让调用者处理 raise ValueError(f"解密失败: {str(e)}") @classmethod def encrypt_dict_value(cls, data: dict, key: str) -> dict: """ 加密字典中指定键的值 Args: data: 字典数据 key: 要加密的键名 Returns: 加密后的字典 """ if key in data and data[key]: data[key] = cls.encrypt(str(data[key])) return data @classmethod def decrypt_dict_value(cls, data: dict, key: str) -> dict: """ 解密字典中指定键的值 Args: data: 字典数据 key: 要解密的键名 Returns: 解密后的字典 """ if key in data and data[key]: try: data[key] = cls.decrypt(str(data[key])) except ValueError: # 如果解密失败,可能是未加密的数据,保持原值 pass return data @classmethod def is_encrypted(cls, text: str) -> bool: """ 判断文本是否已加密 Args: text: 要检查的文本 Returns: 是否已加密 """ if not text: return False try: # 尝试解密,如果成功则说明已加密 cls.decrypt(text) return True except: return False