import json from locust import HttpUser, TaskSet, task, between, tag from urllib.parse import urljoin from cryptography.hazmat.primitives.serialization import pkcs12 from cryptography.hazmat.primitives import serialization from cryptography.hazmat.backends import default_backend import tempfile import urllib3 # ================= 配置区域 ================= # 请根据实际情况修改以下配置,或者在启动 Locust 时通过 --config 参数传入 # 禁用InsecureRequestWarning警告 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) # 1. OAuth2 认证配置 AUTH_URL = "https://ssoalpha.dvb.corpinter.net.cn/v1/token" # 获取Token的相对路径或绝对路径 CLIENT_ID = "D650DB94-FB62-427A-980A-601BBC3081B6" CLIENT_SECRET = "wU2G-xa08SHY_T7N.5BcFv~M94q61D3n" SCOPE = "openid" # 2. 目标业务接口配置 # 截图中的 VIN 码 GEN6_TEST_VIN = "LE4FG1DB1SL002567" GEN5_TEST_VIN = "LE4AG4CB7SLYTKXLG" # 业务接口路径 (截图中的 /api/vehicles/{vin}/statistics) API_PATH = "/api/vehicles/{vin}/statistics" Baumuster = "174123" # ============================================ class UserBehavior(TaskSet): access_token = None key_store_file_path = "mic_certificate_W1AFJ10000Z000085.pfx" key_store_passphrase = "b04ca3c0-518d-40b0-a0d5-01c0c25350b9" def on_start(self): """ 虚拟用户启动时执行:获取 Access Token """ self.get_access_token() self.setup_client_certificate() def get_access_token(self): """ 使用 Client Credentials 模式获取 Token """ # 构造请求体,OAuth2 标准格式 payload = { "grant_type": "client_credentials", "client_id": self.user.client_id, "client_secret": self.user.client_secret, "scope": self.user.scope } # 发送 POST 请求获取 Token # 注意:这里使用 self.client.post,意味着它会使用 HttpUser 中定义的 host with self.client.post( AUTH_URL, data=payload, name="OAuth_GetToken", # 在报告中统一命名为 OAuth_GetToken catch_response=True # 允许手动控制成功/失败 ) as response: if response.status_code == 200: try: json_resp = response.json() # 假设返回结构中有 access_token 字段 self.__class__.access_token = json_resp.get("access_token") if self.__class__.access_token: response.success() print(f"\n[成功] 获取到 Token: {self.__class__.access_token[:10]}...") else: response.failure("响应中未找到 access_token") except json.JSONDecodeError: response.failure("响应不是有效的 JSON") else: response.failure(f"获取 Token 失败,状态码: {response.status_code}") def setup_client_certificate(self): """设置客户端证书用于HTTPS请求""" # 从PFX文件加载私钥和证书 with open(self.key_store_file_path, 'rb') as f: pfx_data = f.read() private_key, certificate, additional_certificates = pkcs12.load_key_and_certificates( pfx_data, self.key_store_passphrase.encode(), backend=default_backend() ) # 将证书和私钥保存为临时PEM文件 cert_temp = tempfile.NamedTemporaryFile(mode='w', suffix='.pem', delete=False) cert_temp.write(certificate.public_bytes(serialization.Encoding.PEM).decode()) cert_temp.close() key_temp = tempfile.NamedTemporaryFile(mode='w', suffix='.pem', delete=False) key_temp.write(private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption() ).decode()) key_temp.close() # 配置requests session使用证书 self.client.cert = (cert_temp.name, key_temp.name) # 禁用SSL验证(在企业内网环境中常见) self.client.verify = False @tag('business_api') @task def get_adas_statistics(self): """ 业务接口:获取车辆统计信息 """ if not self.__class__.access_token: print("没有 Token,无法请求业务接口") return # 构造 Header headers = { "Authorization": f"Bearer {self.__class__.access_token}", "Baumuster": Baumuster } # 格式化 URL,将 VIN 填入路径 # 注意:如果 API_PATH 是绝对路径,urljoin 可能表现不同,建议 host 配置为空或域名,API_PATH 为相对路径 # 这里为了稳健,直接拼接或根据实际情况调整 url = API_PATH.format(vin=self.user.vin) # print(f"\n[开始] 请求业务接口: {url}") # 发送 GET 请求 self.client.get( url, headers=headers, name="GET_AdasStatistics" # 在报告中统一名称 ) class WebsiteUser(HttpUser): tasks = [UserBehavior] # 设置用户等待时间,模拟真实用户操作间隔 (例如 1到5秒) wait_time = between(1, 3) # 从全局变量读取配置,方便命令行覆盖 client_id = CLIENT_ID client_secret = CLIENT_SECRET scope = SCOPE vin = GEN6_TEST_VIN # 如果需要支持 HTTPS 且证书不被信任,可以添加 verify=False verify = False