# -*- coding: utf-8 -*- import json import logging import os import allure import requests CAPTCHA_URL = os.getenv( "JOYHUB_CAPTCHA_URL", "http://test-manager-api.best-envision.com/admin/login/captcha", ) LOGIN_BASE_URL = os.getenv( "JOYHUB_LOGIN_BASE_URL", "http://test-manager-api.best-envision.com", ) LOGIN_PATH = os.getenv("JOYHUB_LOGIN_PATH", "/admin/login/login") USERNAME = os.getenv("JOYHUB_USERNAME", "guojiabao") PASSWORD = os.getenv("JOYHUB_PASSWORD", "gjb123456") CAPTCHA = os.getenv("JOYHUB_CAPTCHA", "1111") TIMEOUT = int(os.getenv("JOYHUB_TIMEOUT", "20")) TENANT_ID = os.getenv("JOYHUB_TENANT_ID", "126") class JoyhubAuth(object): def __init__(self): self.session = requests.Session() self._token = None @property def login_url(self): return LOGIN_BASE_URL.rstrip("/") + LOGIN_PATH def get_captcha_key(self): with allure.step("前置:获取登录验证码 key"): logging.info("GET %s", CAPTCHA_URL) response = self.session.get(CAPTCHA_URL, timeout=TIMEOUT) self._attach_response("captcha", response) response.raise_for_status() data = response.json() key = self._extract_key(data) assert key, "验证码接口未返回 key,响应:{}".format(data) return key def login(self): if self._token: return self._token key = self.get_captcha_key() body = { "key": key, "username": USERNAME, "password": PASSWORD, "captcha": CAPTCHA, } headers = {"Content-Type": "application/json", "tenant-id": TENANT_ID} with allure.step("前置:登录并获取 token"): logging.info("POST %s", self.login_url) logging.info("request headers: %s", headers) logging.info("request body: %s", self._safe_body(body)) response = self.session.post(self.login_url, json=body, headers=headers, timeout=TIMEOUT) self._attach_request(self.login_url, "POST", headers, self._safe_body(body)) self._attach_response("login", response) response.raise_for_status() data = response.json() token = self._extract_token(data) assert token, "登录接口未返回 token,响应:{}".format(data) self._token = token if token.startswith("Bearer ") else "Bearer " + token return self._token def auth_headers(self): return { "Authorization": self.login(), "Content-Type": "application/json", "tenant-id": TENANT_ID, } @staticmethod def _extract_key(data): if isinstance(data, dict): for field in ("key", "captchaKey", "captcha_key", "uuid"): if data.get(field): return data.get(field) nested = data.get("data") if isinstance(nested, dict): for field in ("key", "captchaKey", "captcha_key", "uuid"): if nested.get(field): return nested.get(field) if isinstance(nested, str): return nested return None @staticmethod def _extract_token(data): token_fields = ( "token", "access_token", "accessToken", "Authorization", "authorization", "userToken", "user_token", "jwt", ) if isinstance(data, dict): for field in token_fields: token = data.get(field) if JoyhubAuth._looks_like_token(token): return token nested = data.get("data") if isinstance(nested, dict): for field in token_fields: token = nested.get(field) if JoyhubAuth._looks_like_token(token): return token return None @staticmethod def _looks_like_token(value): if not isinstance(value, str): return False value = value.strip() if not value: return False if any(ord(char) > 127 for char in value): return False return len(value) >= 16 or value.startswith("Bearer ") @staticmethod def _safe_body(body): safe = dict(body) if "password" in safe: safe["password"] = "******" return safe @staticmethod def _attach_request(url, method, headers, body): allure.attach(str(url), "请求url", allure.attachment_type.TEXT) allure.attach(str(method), "请求方式", allure.attachment_type.TEXT) allure.attach(json.dumps(headers, ensure_ascii=False, indent=2), "请求头", allure.attachment_type.JSON) allure.attach(json.dumps(body, ensure_ascii=False, indent=2), "请求体", allure.attachment_type.JSON) @staticmethod def _attach_response(name, response): logging.info("%s response status: %s", name, response.status_code) logging.info("%s response body: %s", name, response.text) allure.attach(str(response.status_code), "{} 响应状态码".format(name), allure.attachment_type.TEXT) allure.attach(response.text, "{} 响应体".format(name), allure.attachment_type.JSON)