QSSS_new / backend /app /core /security.py
misonL's picture
特性:实现对话框、表单、输入框、标签、下拉菜单、分隔线、骨架屏、滑块、表格、文本区域的 UI 组件
c78ce9e
Raw
History Blame Contribute Delete
4.79 kB
from datetime import datetime, timedelta
from typing import Any, Optional
from jose import jwt, JWTError
from passlib.context import CryptContext
import secrets
import string
from app.core.config import settings
from loguru import logger
# 密码加密上下文 (统一在 UserService 中管理)
# pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# OAuth2 密码流 (统一在 AuthService 中管理)
# oauth2_scheme = OAuth2PasswordBearer(
# tokenUrl=f"{settings.API_V1_STR}/auth/login"
# )
def create_access_token(
data: dict, expires_delta: Optional[timedelta] = None
) -> str:
"""
创建访问令牌
Args:
data: 要编码的数据
expires_delta: 过期时间增量
Returns:
JWT令牌字符串
"""
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(
minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES
)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(
to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM
)
return encoded_jwt
def verify_token(token: str) -> Optional[dict]:
"""
验证JWT令牌
Args:
token: JWT令牌
Returns:
解码后的数据或None
"""
try:
payload = jwt.decode(
token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]
)
return payload
except JWTError as e:
logger.warning(f"JWT验证失败: {e}")
return None
def get_password_hash(password: str) -> str:
"""
生成密码哈希 (统一在 UserService 中管理)
"""
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") # 临时创建,因为这里需要
return pwd_context.hash(password)
def verify_password(plain_password: str, hashed_password: str) -> bool:
"""
验证密码 (统一在 UserService 中管理)
"""
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") # 临时创建,因为这里需要
return pwd_context.verify(plain_password, hashed_password)
def generate_random_string(length: int = 32) -> str:
"""
生成随机字符串
"""
alphabet = string.ascii_letters + string.digits
return ''.join(secrets.choice(alphabet) for _ in range(length))
def generate_reset_token() -> str:
"""
生成密码重置令牌
"""
return secrets.token_urlsafe(32)
# 移除 get_user_by_id, get_current_user, get_current_active_user, check_user_permission, require_role, require_admin
# 这些功能现在通过 app.core.deps 和 app.services.user_service/auth_service 提供
class SecurityUtils:
"""
安全工具类
"""
@staticmethod
def mask_email(email: str) -> str:
"""
邮箱脱敏
"""
if "@" not in email:
return email
local, domain = email.split("@", 1)
if len(local) <= 2:
masked_local = "*" * len(local)
else:
masked_local = local[0] + "*" * (len(local) - 2) + local[-1]
return f"{masked_local}@{domain}"
@staticmethod
def mask_phone(phone: str) -> str:
"""
手机号脱敏
"""
if len(phone) < 7:
return phone
return phone[:3] + "****" + phone[-4:]
@staticmethod
def validate_password_strength(password: str) -> dict:
"""
验证密码强度
"""
result = {
"valid": True,
"score": 0,
"issues": []
}
# 长度检查
if len(password) < 8:
result["valid"] = False
result["issues"].append("密码长度至少8位")
else:
result["score"] += 1
# 包含数字
if any(c.isdigit() for c in password):
result["score"] += 1
else:
result["issues"].append("密码应包含数字")
# 包含小写字母
if any(c.islower() for c in password):
result["score"] += 1
else:
result["issues"].append("密码应包含小写字母")
# 包含大写字母
if any(c.isupper() for c in password):
result["score"] += 1
else:
result["issues"].append("密码应包含大写字母")
# 包含特殊字符
special_chars = "!@#$%^&*()_+-=[]{}|;:,.<>?"
if any(c in special_chars for c in password):
result["score"] += 1
else:
result["issues"].append("密码应包含特殊字符")
# 最终验证
if result["score"] < 3:
result["valid"] = False
return result
# 创建安全工具实例
security_utils = SecurityUtils()