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()