"""鼎捷云 IAM token 鉴权服务 通过调用 IAM `/api/iam/v2/identity/token/analyze` 校验请求头中的 digi-middleware-auth-user / digi-middleware-auth-app,解析出用户信息。 进程内 LRU 缓存(userToken -> userInfo),TTL 由 IAM_CACHE_TTL 控制, 减少对 IAM 的重复调用。 """ import time import httpx from .config import settings # httpx 异步客户端(进程级单例,复用连接池) _http_client: httpx.AsyncClient | None = None # 缓存:userToken -> (user_info, fetched_at) _cache: dict[str, tuple[dict | None, float]] = {} def _get_http_client() -> httpx.AsyncClient: global _http_client if _http_client is None: _http_client = httpx.AsyncClient(timeout=5.0) return _http_client async def close_iam_client() -> None: """关闭 httpx 客户端(进程退出时调用)。""" global _http_client if _http_client is not None: await _http_client.aclose() _http_client = None async def analyze_token(user_token: str, app_token: str | None = None) -> dict | None: """校验 IAM userToken,返回用户信息 dict 或 None。 调用 POST {IAM_BASE_URL}/api/iam/v2/identity/token/analyze, 请求头携带 digi-middleware-auth-app 与 digi-middleware-auth-user。 成功时返回包含 id/name/tenantId/tenantName 等字段的 dict。 """ # 1. 查缓存 now = time.time() cached = _cache.get(user_token) if cached is not None and (now - cached[1]) < settings.IAM_CACHE_TTL: return cached[0] # 2. 调用 IAM analyze headers = { "digi-middleware-auth-user": user_token, "digi-middleware-auth-app": app_token or settings.IAM_APP_TOKEN, } try: client = _get_http_client() resp = await client.post( f"{settings.IAM_BASE_URL}/api/iam/v2/identity/token/analyze", headers=headers, ) except Exception as ex: # IAM 不可达,缓存短时间避免雪崩 _cache[user_token] = (None, now) print(f"[IAM] analyze 请求异常: {ex}") return None if resp.status_code == 200: data = resp.json() # 兼容字段:id(用户标识)/ name(姓名) if data.get("id") or data.get("name"): _cache[user_token] = (data, now) return data # 返回体无用户标识,视为无效 _cache[user_token] = (None, now) return None # 非 200(token 无效/过期),缓存避免雪崩 _cache[user_token] = (None, now) return None