Files
FastapiAdmin/backend/app/utils/hash_bcrpy_util.py
T
zhangtao d6c3104f2d chore: upgrade python version to 3.12 and refactor password encryption
This commit upgrades the project's Python base version from 3.10 to 3.12, updates all related dependency versions, replaces bcrypt password hashing with pbkdf2-sha256, refactors the UA parsing library, removes unused dependencies, and fixes related exception handling code.
2026-06-21 06:57:44 +08:00

86 lines
3.2 KiB
Python

import base64
import hashlib
import os
from cryptography.hazmat.backends.openssl import backend
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
_PBKDF2_ALGO = "sha256"
_PBKDF2_ITERATIONS = 600_000
_PBKDF2_SALT_LEN = 16
_PBKDF2_PREFIX = "$pbkdf2-sha256$"
class PwdUtil:
@staticmethod
def hash_password(password: str) -> str:
salt = os.urandom(_PBKDF2_SALT_LEN)
dk = hashlib.pbkdf2_hmac(_PBKDF2_ALGO, password.encode(), salt, _PBKDF2_ITERATIONS)
return f"{_PBKDF2_PREFIX}{_PBKDF2_ITERATIONS}${base64.b64encode(salt).decode()}${base64.b64encode(dk).decode()}"
@staticmethod
def verify_password(plain_password: str, password_hash: str) -> bool:
try:
_, _algo, iters_str, salt_b64, hash_b64 = password_hash.split("$")
salt = base64.b64decode(salt_b64)
expected = base64.b64decode(hash_b64)
dk = hashlib.pbkdf2_hmac(_PBKDF2_ALGO, plain_password.encode(), salt, int(iters_str))
return dk == expected
except Exception:
return False
@staticmethod
def check_password_strength(password: str) -> str | None:
if len(password) < 6:
return "密码长度至少6位"
if not any(c.isupper() for c in password):
return "密码需要包含大写字母"
if not any(c.islower() for c in password):
return "密码需要包含小写字母"
if not any(c.isdigit() for c in password):
return "密码需要包含数字"
return None
class AESCipher:
"""AES 加密器"""
def __init__(self, key: bytes | str) -> None:
self.key = key if isinstance(key, bytes) else bytes.fromhex(key)
def encrypt(self, plaintext: bytes | str) -> bytes:
if not isinstance(plaintext, bytes):
plaintext = str(plaintext).encode("utf-8")
iv = os.urandom(16)
cipher = Cipher(algorithms.AES(self.key), modes.CBC(iv), backend=backend)
encryptor = cipher.encryptor()
padder = padding.PKCS7(cipher.algorithm.block_size).padder() # type: ignore
padded_plaintext = padder.update(plaintext) + padder.finalize()
ciphertext = encryptor.update(padded_plaintext) + encryptor.finalize()
return iv + ciphertext
def decrypt(self, ciphertext: bytes | str) -> str:
ciphertext = ciphertext if isinstance(ciphertext, bytes) else bytes.fromhex(ciphertext)
iv = ciphertext[:16]
ciphertext = ciphertext[16:]
cipher = Cipher(algorithms.AES(self.key), modes.CBC(iv), backend=backend)
decryptor = cipher.decryptor()
unpadder = padding.PKCS7(cipher.algorithm.block_size).unpadder() # type: ignore
padded_plaintext = decryptor.update(ciphertext) + decryptor.finalize()
plaintext = unpadder.update(padded_plaintext) + unpadder.finalize()
return plaintext.decode("utf-8")
class Md5Cipher:
"""MD5 加密器"""
@staticmethod
def encrypt(plaintext: bytes | str) -> str:
md5 = hashlib.md5()
if not isinstance(plaintext, bytes):
plaintext = str(plaintext).encode("utf-8")
md5.update(plaintext)
return md5.hexdigest()