Files
auto-pickup-v2/login_helper.py
T
SkyLiCoder 2fbb7fc8a5 feat: filter cookies to only store required 10 based on HAR analysis
- Define REQUIRED_COOKIE_NAMES in login_helper.py (10 cookies from HAR)
- login_helper.py: import-time filtering + strict all-or-nothing validation
- pickup_api.py: save_session() filters to whitelist only (prevents accumulation)
- engine.py: unified strict check, all 10 cookies required

Based on portal-to-pickup-full-flow.har analysis:
  - All /pickup-web/a/* APIs use exactly 9 cookies
  - JSESSIONID additionally needed for portal page fetch (Referer)
  - enable_HFZrs6pXKulz is portal UI only, excluded
2026-06-12 11:22:32 +08:00

113 lines
3.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""登录助手 — 解析浏览器导出的 cookies JSON,保存为 session.json。
Cookie 白名单基于 HAR 抓包分析:
- pickup-web 所有业务 API 统一使用 9 个 cookie(见 _REQUIRED_COOKIE_NAMES)
- JSESSIONID 仅 portal 主页面需要(加载 pickup 页面时作为 Referer)
- enable_HFZrs6pXKulz 仅 portal UI 配置,pickup-web 不需要
"""
from __future__ import annotations
import json
import os
import sys
# 基于 portal-to-pickup-full-flow.har 分析得出的必需 cookie
# pickup-web (/pickup-web/a/*) 所有业务 API 请求都携带这 10 个 cookie
# 缺失任何一个都会导致请求失败
REQUIRED_COOKIE_NAMES: set[str] = {
# 会话认证(核心 — 缺失则 401/重定向到登录页)
"jdpt.session.id",
# 组织层级(固定值,session 生命周期内不变,缺失则权限异常)
"CORAL_GRAYLEVEL",
"PROV_GRAYLEVEL",
"CITY_GRAYLEVEL",
"COUNTY_GRAYLEVEL",
"ORG_GRAYLEVEL",
# $_ts 安全框架 token(缺失则 c93l5Fya 校验失败,接口拒绝服务)
"HFZrs6pXKulzS", # 会话级 token,固定不变
"HFZrs6pXKulzO", # 组织级 token,固定不变
"HFZrs6pXKulzP", # 请求级动态 token,服务端 Set-Cookie 持续更新
# portal 主页面会话(缺失则无法加载 pickup 页面,fetch_page_resources 失败)
"JSESSIONID",
}
def _is_cookie_required(name: str) -> bool:
"""判断 cookie 是否为 pickup 流程所必需。"""
return name in REQUIRED_COOKIE_NAMES
def get_session_path() -> str:
if getattr(sys, "frozen", False):
base = os.path.dirname(sys.executable)
else:
base = os.path.dirname(os.path.abspath(__file__))
return os.path.join(base, "session.json")
def parse_json_and_save(json_text: str) -> int:
"""解析浏览器导出的 cookies JSON 数组,过滤并保存必需 cookie。
只保存 _REQUIRED_COOKIE_NAMES 白名单中的 cookie(基于 HAR 分析)。
返回保存的 cookie 数量。
"""
raw = json.loads(json_text)
if not isinstance(raw, list):
raise ValueError("JSON 必须是数组格式")
# 过滤目标域名 + 必需 cookie 名称
target = "10.4.188.1"
cookies = []
seen = set()
skipped = []
for c in raw:
domain = c.get("domain", "")
if target not in domain:
continue
name = c.get("name", "").strip()
path = c.get("path", "/")
if not name:
continue
if not _is_cookie_required(name):
skipped.append(name)
continue
key = (name, domain, path)
if key in seen:
continue
seen.add(key)
cookies.append({
"name": name,
"value": c.get("value", ""),
"domain": domain,
"path": path,
})
# 检查必需 cookie 是否齐全(全部 10 个缺一不可)
found_names = {c["name"] for c in cookies}
missing = REQUIRED_COOKIE_NAMES - found_names
if missing:
raise ValueError(
f"缺少必需 cookie ({len(missing)} 个): {', '.join(sorted(missing))}"
)
data = {
"cookies": cookies,
"saved_from": f"https://{target}",
"filtered_count": len(skipped),
}
session_path = get_session_path()
os.makedirs(os.path.dirname(session_path) or ".", exist_ok=True)
with open(session_path, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
return len(cookies)
def load_session_info() -> dict | None:
"""读取已保存的 session 信息用于 UI 展示。"""
try:
with open(get_session_path(), "r") as f:
return json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
return None