前后端第一版提交

This commit is contained in:
2025-11-11 17:21:59 +08:00
commit 96e9a6d396
241 changed files with 197906 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
from .encryption_utils import EncryptionUtils
from .password_hashers import MD5PasswordHasher
__all__ = ['EncryptionUtils', 'MD5PasswordHasher']

View File

@@ -0,0 +1,160 @@
import base64
import hashlib
import secrets
from typing import Optional
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
class EncryptionUtils:
"""
加密工具类
提供各种加密解密功能
"""
@staticmethod
def generate_salt(length: int = 32) -> str:
"""
生成随机盐值
Args:
length: 盐值长度
Returns:
str: Base64编码的盐值
"""
salt = secrets.token_bytes(length)
return base64.b64encode(salt).decode('utf-8')
@staticmethod
def md5_hash(data: str, salt: str = '') -> str:
"""
MD5哈希加密
Args:
data: 待加密数据
salt: 盐值
Returns:
str: MD5哈希值
"""
combined = data + salt
md5_hash = hashlib.md5(combined.encode('utf-8'))
return md5_hash.hexdigest()
@staticmethod
def sha256_hash(data: str, salt: str = '') -> str:
"""
SHA256哈希加密
Args:
data: 待加密数据
salt: 盐值
Returns:
str: SHA256哈希值
"""
combined = data + salt
sha256_hash = hashlib.sha256(combined.encode('utf-8'))
return sha256_hash.hexdigest()
@staticmethod
def generate_key_from_password(password: str, salt: bytes) -> bytes:
"""
从密码生成加密密钥
Args:
password: 密码
salt: 盐值
Returns:
bytes: 加密密钥
"""
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=100000,
)
key = base64.urlsafe_b64encode(kdf.derive(password.encode()))
return key
@staticmethod
def encrypt_data(data: str, password: str) -> Optional[str]:
"""
使用密码加密数据
Args:
data: 待加密数据
password: 加密密码
Returns:
Optional[str]: 加密后的数据Base64编码失败返回None
"""
try:
# 生成随机盐值
salt = secrets.token_bytes(16)
# 从密码生成密钥
key = EncryptionUtils.generate_key_from_password(password, salt)
# 创建Fernet实例
fernet = Fernet(key)
# 加密数据
encrypted_data = fernet.encrypt(data.encode('utf-8'))
# 将盐值和加密数据组合
combined = salt + encrypted_data
# 返回Base64编码的结果
return base64.b64encode(combined).decode('utf-8')
except Exception:
return None
@staticmethod
def decrypt_data(encrypted_data: str, password: str) -> Optional[str]:
"""
使用密码解密数据
Args:
encrypted_data: 加密后的数据Base64编码
password: 解密密码
Returns:
Optional[str]: 解密后的数据失败返回None
"""
try:
# Base64解码
combined = base64.b64decode(encrypted_data.encode('utf-8'))
# 分离盐值和加密数据
salt = combined[:16]
encrypted_bytes = combined[16:]
# 从密码生成密钥
key = EncryptionUtils.generate_key_from_password(password, salt)
# 创建Fernet实例
fernet = Fernet(key)
# 解密数据
decrypted_data = fernet.decrypt(encrypted_bytes)
return decrypted_data.decode('utf-8')
except Exception:
return None
@staticmethod
def generate_random_key() -> str:
"""
生成随机加密密钥
Returns:
str: Base64编码的随机密钥
"""
key = Fernet.generate_key()
return key.decode('utf-8')

View File

@@ -0,0 +1,79 @@
import hashlib
from django.contrib.auth.hashers import BasePasswordHasher
from django.utils.crypto import constant_time_compare
class MD5PasswordHasher(BasePasswordHasher):
"""
MD5密码哈希器
用于兼容旧系统的MD5密码加密
"""
algorithm = 'md5'
library = 'hashlib'
def encode(self, password, salt):
"""
编码密码
Args:
password: 原始密码
salt: 盐值
Returns:
str: 编码后的密码
"""
hash_obj = hashlib.md5((salt + password).encode('utf-8'))
hash_value = hash_obj.hexdigest()
return f'{self.algorithm}${salt}${hash_value}'
def verify(self, password, encoded):
"""
验证密码
Args:
password: 原始密码
encoded: 编码后的密码
Returns:
bool: 验证结果
"""
algorithm, salt, hash_value = encoded.split('$', 2)
assert algorithm == self.algorithm
encoded_2 = self.encode(password, salt)
return constant_time_compare(encoded, encoded_2)
def safe_summary(self, encoded):
"""
返回密码的安全摘要信息
Args:
encoded: 编码后的密码
Returns:
dict: 摘要信息
"""
algorithm, salt, hash_value = encoded.split('$', 2)
assert algorithm == self.algorithm
return {
'algorithm': algorithm,
'salt': salt[:6] + '...',
'hash': hash_value[:6] + '...',
}
def harden_runtime(self, password, encoded):
"""
硬化运行时间MD5不需要
"""
pass
def must_update(self, encoded):
"""
检查是否需要更新密码编码
Args:
encoded: 编码后的密码
Returns:
bool: 是否需要更新
"""
return False