import json import time from typing import Optional, Dict, Any from datetime import datetime, timedelta from apitest.models.exceptions import AuthException class AuthManager: """认证管理器""" def __init__(self, base_url: str, credentials: Dict[str, str], logger): """初始化认证管理器 Args: base_url: 基础URL credentials: 认证凭据 logger: 日志记录器 """ self.base_url = base_url.rstrip("/") self.credentials = credentials self.logger = logger self._token: Optional[str] = None self._token_expiry: Optional[datetime] = None self._refresh_token: Optional[str] = None self._login_endpoint: str = "/sys/auth/login" def set_login_endpoint(self, endpoint: str): """设置登录端点 Args: endpoint: 登录端点 """ self._login_endpoint = endpoint def login(self, login_endpoint: Optional[str] = None) -> Dict[str, Any]: """执行登录操作 Args: login_endpoint: 登录端点,默认使用配置的端点 Returns: 登录响应数据 Raises: AuthException: 登录失败时抛出 """ endpoint = login_endpoint or self._login_endpoint url = f"{self.base_url}{endpoint}" self.logger.info(f"尝试登录: {url}") try: import requests response = requests.post( url, json={ "username": self.credentials.get("username", ""), "password": self.credentials.get("password", "") }, timeout=10 ) if response.status_code == 200: data = response.json() if "data" in data and "token" in data["data"]: self._token = data["data"]["token"] self._refresh_token = data["data"].get("refreshToken") expiry_seconds = data["data"].get("expiresIn", 3600) self._token_expiry = datetime.now() + timedelta(seconds=expiry_seconds) self.logger.info("登录成功") return data else: raise AuthException("登录响应中未找到token") else: raise AuthException(f"登录失败: HTTP {response.status_code}") except requests.RequestException as e: raise AuthException(f"登录请求失败: {e}") def get_token(self) -> Optional[str]: """获取当前token Returns: 当前token,如果未登录则返回None """ return self._token def set_token(self, token: str, expiry_seconds: int = 3600): """设置token Args: token: 认证令牌 expiry_seconds: 过期时间(秒),默认3600秒 """ self._token = token self._token_expiry = datetime.now() + timedelta(seconds=expiry_seconds) self.logger.info("Token已设置") def is_token_valid(self) -> bool: """检查token是否有效 Returns: token是否有效 """ if not self._token or not self._token_expiry: return False return datetime.now() < self._token_expiry def refresh_token(self) -> bool: """刷新token Returns: 刷新是否成功 """ if not self._refresh_token: self.logger.warning("没有可用的refresh token") return False try: import requests url = f"{self.base_url}/sys/auth/refresh" response = requests.post( url, json={"refreshToken": self._refresh_token}, timeout=10 ) if response.status_code == 200: data = response.json() if "data" in data and "token" in data["data"]: self._token = data["data"]["token"] self._refresh_token = data["data"].get("refreshToken") expiry_seconds = data["data"].get("expiresIn", 3600) self._token_expiry = datetime.now() + timedelta(seconds=expiry_seconds) self.logger.info("Token刷新成功") return True self.logger.warning("Token刷新失败,尝试重新登录") return False except Exception as e: self.logger.error(f"Token刷新异常: {e}") return False def ensure_authenticated(self) -> str: """确保已认证,如果token过期则自动刷新或重新登录 Returns: 有效的token Raises: AuthException: 认证失败时抛出 """ if not self._token: self.login() elif not self.is_token_valid(): if not self.refresh_token(): self.login() if not self._token: raise AuthException("无法获取有效的认证token") return self._token def logout(self): """登出""" self._token = None self._refresh_token = None self._token_expiry = None self.logger.info("已登出") def get_auth_headers(self) -> Dict[str, str]: """获取认证请求头 Returns: 包含认证信息的请求头字典 """ token = self.ensure_authenticated() return { "Authorization": f"Bearer {token}", "Content-Type": "application/json" } def set_credentials(self, username: str, password: str): """设置认证凭据 Args: username: 用户名 password: 密码 """ self.credentials = {"username": username, "password": password} self.logger.info("认证凭据已更新")