- 新增小红书前端模块(启动服务、内容分析、爆款创作、发布记录、技能卡片、拟人设置、AI 助手) - 新增 xhs-agent-desktop 独立桌面 Agent,对接 OMS 任务调度 - 登录页重构,整合微信登录与 OMS JWT 认证 - 订单、渠道、用户中心等页面与 API 增强 Co-authored-by: Cursor <cursoragent@cursor.com>
85 lines
3.3 KiB
Python
85 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Verify Alibaba AOP signatureWithParamsAndUrlPath against a known-good URL."""
|
|
import hashlib
|
|
import hmac
|
|
from urllib.parse import unquote
|
|
|
|
EXPECTED = "c950e08db859f1d3899d88a30dc2b8b46e5b3394"
|
|
URL_PATH = "param2/1/ali.intl.onetouch/logistics.order.notifyBizEvent/842172"
|
|
|
|
# Exact notifyBizEventDTO value from the working URL (query-decoded once)
|
|
DTO_FROM_URL = unquote(
|
|
"%7B%22aliOrderNo%22%3A%22ALS01691955122%22%2C%22apiMessageCode%22%3A31%2C"
|
|
"%22eventCode%22%3A%22WAREHOUSE_INBOUND%22%2C%22eventTime%22%3A%222026-06-06T09%3A24%3A50%2B0800%22%2C"
|
|
"%22extInfo%22%3A%22%22%2C%22location%22%3A%22CHINA+SHENZHEN%22%2C%22reason%22%3A%22%22%2C%22status%22%3A%22SUCCESS%22%7D"
|
|
)
|
|
|
|
# C# JsonConvert.SerializeObject often uses space after colon/comma
|
|
DTO_CSHARP_LIKE = (
|
|
'{"aliOrderNo":"ALS01691955122","apiMessageCode":31,"eventCode":"WAREHOUSE_INBOUND",'
|
|
'"eventTime":"2026-06-06T09:24:50+0800","extInfo":"","location":"CHINA SHENZHEN",'
|
|
'"reason":"","status":"SUCCESS"}'
|
|
)
|
|
|
|
# location with + instead of space (as in URL before decode)
|
|
DTO_LOCATION_PLUS = DTO_FROM_URL.replace("CHINA SHENZHEN", "CHINA+SHENZHEN")
|
|
|
|
|
|
def hmac_sha1_hex_lower(secret: str, data: str) -> str:
|
|
return hmac.new(secret.encode("utf-8"), data.encode("utf-8"), hashlib.sha1).hexdigest()
|
|
|
|
|
|
def hmac_sha1_hex_upper(secret: str, data: str) -> str:
|
|
return hmac_sha1_hex_lower(secret, data).upper()
|
|
|
|
|
|
def sign_url_path_params(secret: str, url_path: str, params: dict[str, str]) -> str:
|
|
"""1688 CommonUtil.signatureWithParamsAndUrlPath style."""
|
|
parts = [url_path]
|
|
for key in sorted(params.keys()):
|
|
parts.append(key + params[key])
|
|
sign_str = "".join(parts)
|
|
return hmac_sha1_hex_lower(secret, sign_str), sign_str
|
|
|
|
|
|
def sign_py_style(secret: str, url_path: str, dto: str) -> tuple[str, str, str]:
|
|
"""Match E:/py脚本/平台exe/al_tax_core.py sign()."""
|
|
param_list = ["notifyBizEventDTO" + dto]
|
|
param_list.sort()
|
|
sign_str = url_path + "".join(param_list)
|
|
digest = hmac.new(secret.encode("utf-8"), sign_str.encode("utf-8"), hashlib.sha1).hexdigest()
|
|
return digest, digest.upper(), sign_str
|
|
|
|
|
|
def main() -> None:
|
|
import sys
|
|
|
|
secret = sys.argv[1] if len(sys.argv) > 1 else "5VGqKYWs4UOA"
|
|
candidates = {
|
|
"dto_from_url": DTO_FROM_URL,
|
|
"dto_csharp_like": DTO_CSHARP_LIKE,
|
|
"dto_location_plus": DTO_LOCATION_PLUS,
|
|
}
|
|
|
|
print("Expected:", EXPECTED)
|
|
print("Secret:", secret[:4] + "***")
|
|
for label, dto in candidates.items():
|
|
lower_sorted, sign_str = sign_url_path_params(
|
|
secret, URL_PATH, {"notifyBizEventDTO": dto}
|
|
)
|
|
py_lower, py_upper, py_str = sign_py_style(secret, URL_PATH, dto)
|
|
match_lower = lower_sorted == EXPECTED
|
|
match_py_lower = py_lower == EXPECTED
|
|
match_py_upper = py_upper.lower() == EXPECTED
|
|
print(f"\n[{label}]")
|
|
print(" sorted-dict lower:", lower_sorted, "MATCH" if match_lower else "")
|
|
print(" py-style lower:", py_lower, "MATCH" if match_py_lower else "")
|
|
print(" py-style upper:", py_upper, "MATCH" if py_upper == EXPECTED else "")
|
|
print(" sign_str len:", len(py_str))
|
|
if py_str != sign_str:
|
|
print(" NOTE: sign_str differs between implementations")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|