80 lines
2.4 KiB
Python
80 lines
2.4 KiB
Python
import secrets
|
||
from loguru import logger
|
||
from argon2 import PasswordHasher
|
||
from argon2.exceptions import VerifyMismatchError
|
||
from enum import StrEnum
|
||
|
||
_ph = PasswordHasher()
|
||
|
||
class PasswordStatus(StrEnum):
|
||
"""密码校验状态枚举"""
|
||
|
||
VALID = "valid"
|
||
"""密码校验通过"""
|
||
|
||
INVALID = "invalid"
|
||
"""密码校验失败"""
|
||
|
||
EXPIRED = "expired"
|
||
"""密码哈希已过时,建议重新哈希"""
|
||
|
||
class Password:
|
||
"""密码处理工具类,包含密码生成、哈希和验证功能"""
|
||
|
||
@staticmethod
|
||
def generate(
|
||
length: int = 8
|
||
) -> str:
|
||
"""
|
||
生成指定长度的随机密码。
|
||
|
||
:param length: 密码长度
|
||
:type length: int
|
||
:return: 随机密码
|
||
:rtype: str
|
||
"""
|
||
return secrets.token_hex(length)
|
||
|
||
@staticmethod
|
||
def hash(
|
||
password: str
|
||
) -> str:
|
||
"""
|
||
使用 Argon2 生成密码的哈希值。
|
||
|
||
返回的哈希字符串已经包含了所有需要验证的信息(盐、算法参数等)。
|
||
|
||
:param password: 需要哈希的原始密码
|
||
:return: Argon2 哈希字符串
|
||
"""
|
||
return _ph.hash(password)
|
||
|
||
@staticmethod
|
||
def verify(
|
||
hash: str,
|
||
password: str
|
||
) -> PasswordStatus:
|
||
"""
|
||
验证存储的 Argon2 哈希值与用户提供的密码是否匹配。
|
||
|
||
:param hash: 数据库中存储的 Argon2 哈希字符串
|
||
:param password: 用户本次提供的密码
|
||
:return: 如果密码匹配返回 True, 否则返回 False
|
||
"""
|
||
try:
|
||
# verify 函数会自动解析 stored_password 中的盐和参数
|
||
_ph.verify(hash, password)
|
||
|
||
# 检查哈希参数是否已过时。如果返回True,
|
||
# 意味着你应该使用新的参数重新哈希密码并更新存储。
|
||
# 这是一个很好的实践,可以随着时间推移增强安全性。
|
||
if _ph.check_needs_rehash(hash):
|
||
logger.warning("密码哈希参数已过时,建议重新哈希并更新。")
|
||
return PasswordStatus.EXPIRED
|
||
|
||
return PasswordStatus.VALID
|
||
except VerifyMismatchError:
|
||
# 这是预期的异常,当密码不匹配时触发。
|
||
return PasswordStatus.INVALID
|
||
# 其他异常(如哈希格式错误)应该传播,让调用方感知系统问题
|