From 3b3f16a875eefeac05570ca81c67a7bb9b183f3c Mon Sep 17 00:00:00 2001 From: ZhuJW <1421267742@qq.com> Date: Wed, 29 Apr 2026 18:05:28 +0800 Subject: [PATCH] =?UTF-8?q?cbcs=20adas=20statics=20=E5=8E=8B=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cbcs_statics.py | 156 ++++++++++++++++++++++++++ mic_certificate_W1AFJ10000Z000085.pfx | Bin 0 -> 2789 bytes 2 files changed, 156 insertions(+) create mode 100644 cbcs_statics.py create mode 100644 mic_certificate_W1AFJ10000Z000085.pfx diff --git a/cbcs_statics.py b/cbcs_statics.py new file mode 100644 index 0000000..09cb308 --- /dev/null +++ b/cbcs_statics.py @@ -0,0 +1,156 @@ +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 \ No newline at end of file diff --git a/mic_certificate_W1AFJ10000Z000085.pfx b/mic_certificate_W1AFJ10000Z000085.pfx new file mode 100644 index 0000000000000000000000000000000000000000..7267f01c3912a6beb15669674cdb1a2379559482 GIT binary patch literal 2789 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