海信商定天下商业管理平台接口分析

1. 系统界面

海信商定天下是一套面向大型连锁商业集团的业务管理类软件。

直接在搜索引擎搜索“商定天下”可看到处于公网中的系统,如下图所示。

搜索结果

点进任一系统,登录界面如下图所示。

登录界面

2. 登录接口分析

1、打开 DevTools,分析登录请求的 Payload。这里通过筛选出 DocXHR 类型来快速定位登录请求,如下图所示。

这里可以看到,在登录载荷中,有6个参数,其中一个是我们输入的验证码,1个是我们输入的用户名,1个为空,那我们主要关注剩下的3个即可。

登录payload

2、全局搜索载荷中的 uuid,发现其对应我们输入的验证码。

根据经验,这种 uuid 一般是本地生成,然后请求服务器创建一个验证码与其绑定。

我们可以通过自造 uuid 看是否能正常生成验证码图片来验证,经验证,可以正常生成验证码。

uuid来源

3、接着我们分析 pwd 字段是如何加密的,看到这种带等于号的字符串,先猜其是 Base64 编码,尝试解码发现失败,那有可能是在编码之前做了其他加密。

我们点击登录请求,并切换到 Initiator 选项卡查看该请求的调用栈,从带名字的方法入手查看分析源代码。

登录调用栈

发现加密逻辑为:使用 AES 进行对称加密,CBC 模式,PKCS7 填充,密钥和向量全部使用 tokenKey

密码加密算法

这里的tokenKey我们可以(点击行号位置)在此处下断点,重新登录,获取tokenKey的值,全局搜索一下,看是否其他请求响应的。

获取tokenKey

这里发现 tokenKeyuserKey(剩下的一个未知参数) 都是通过一个get请求获取的。

key的获取

至此,所有参数来源我们都已经知道,这时就可以写代码来测试了。

3、测试登录接口

编写Python代码,对上述分析过程进行实验验。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import base64
import json
import time
import uuid
from urllib.parse import urlencode, urljoin

import requests
import ddddocr
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from loguru import logger


def ocr_by_bytes(content: bytes) -> str:
    """识别验证码字节序列"""
    ocr = ddddocr.DdddOcr(show_ad=False)
    return ocr.classification(content)


def ocr_by_url(url: str) -> str:
    """识别验证码链接"""
    image_bytes = requests.get(url, timeout=5).content
    return ocr_by_bytes(image_bytes)


class Hisense:
    def __init__(self) -> None:
        self.username = None
        self.password = None
        self.uuid = str(uuid.uuid4())
        self.host = "http://x.x.x.x:x/"
        self.captcha_api = urljoin(self.host, "/gatewayserver/authcenter/code/create")
        self.aes_key_api = urljoin(self.host, "/gatewayserver/authcenter/key/get")
        self.login_api = urljoin(self.host, "/gatewayserver/authcenter/token/login")
        self.session = requests.Session()
        self.timeout = 30
        self.session.headers.update(
            {
                "Accept": "application/json, text/plain, */*",
                "Accept-Encoding": "gzip, deflate",
                "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7",
                "appName": "undefined",
                "Cache-Control": "no-cache",
                "Connection": "keep-alive",
                "Cookie": "",
                "funcCode": "home",
                "Pragma": "no-cache",
                "timestamp": str(int(time.time() * 1000)),
                "token": "null",
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36",
            }
        )


    def aes_encrypt(self, plain_text: str, key: bytes) -> str:
        """AES加密,加密密码使用"""
        if isinstance(key, str):
            key = key.encode()
        iv = key
        cipher = AES.new(key, AES.MODE_CBC, iv)
        padded_text = pad(plain_text.encode(), AES.block_size)
        cipher_text = cipher.encrypt(padded_text)
        return base64.b64encode(cipher_text).decode()

    def get_aes_key(self) -> None:
        """从服务器获取 aes key"""
        try:
            aes_info = self.session.get(self.aes_key_api, timeout=self.timeout).json()
            if aes_info.get("code") == 0:
                self.token_key = aes_info["meta"]["tokenKey"]
                self.user_key = aes_info["meta"]["userKey"]
                logger.info(f"token_key:{self.token_key}, user_key:{self.user_key}")
            else:
                logger.error(f"aes响应码不为0: {aes_info}")
        except Exception as e:
            raise Exception("获取aes-key失败") from e

    def get_captcha(self) -> None:
        """获取并识别验证码"""
        params = {
            "uuid": self.uuid,
        }
        query_string = urlencode(params)
        captcha_url = f"{self.captcha_api}?{query_string}"
        try:
            captcha_code = ocr_by_url(captcha_url)
            self.captcha_code = captcha_code
            logger.info(f"已识别验证码: {captcha_code}")
        except Exception as e:
            raise Exception("验证码识别失败") from e

    def login(self) -> bool:
        """自动登录,需要先设置 `self.username` 和 `self.password`"""
        if not (self.username and self.password):
            raise Exception("用户名或密码为空,请先使用 `Hisense.username=xxx` 和 `Hisense.password=xxx` 进行设置")
        self.get_aes_key()
        self.get_captcha()
        encrypted_password = self.aes_encrypt(self.password, self.token_key)
        payload = {
            "code": self.captcha_code,
            "key": self.uuid,
            "loginInfo": self.username,
            "pwd": encrypted_password,
            "tenantCode": "",
            "userKey": self.user_key,
        }
        try:
            login_info = self.session.post(self.login_api, json=payload, timeout=self.timeout).json()
            if login_info.get("code") == 0:
                login_data = login_info["meta"]["data"]
                login_token = login_info["meta"]["token"]
                user_info = json.loads(login_data)
                org_name = user_info["orgName"]
                user_code = user_info["userCode"]
                username = user_info["userName"]
                self.session.headers.update({"appid": user_code})
                self.session.headers.update({"token": login_token})
                logger.info(f"登录成功,[{org_name}][{username}][{user_code}]")
                return True
        except Exception as e:
            raise Exception("登录失败") from e
        return False


if __name__ == "__main__":
    hisense = Hisense()
    hisense.username = "1234"
    hisense.password = "1234"
    hisense.login()

测试效果如下图所示。

接口登录验证

updatedupdated2026-02-052026-02-05