Files
auto-pickup-v2/login_helper.py
T

114 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 抓包分析:
- 必需 cookie 见 REQUIRED_COOKIE_NAMES,可选 cookie 见 OPTIONAL_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
# 导入及会话检查要求以下 9 个 cookie 齐全
REQUIRED_COOKIE_NAMES: set[str] = {
# 会话认证(核心 — 缺失则 401/重定向到登录页)
"jdpt.session.id",
# 组织层级(固定值,session 生命周期内不变,缺失则权限异常)
"CORAL_GRAYLEVEL",
"PROV_GRAYLEVEL",
"CITY_GRAYLEVEL",
"COUNTY_GRAYLEVEL",
"ORG_GRAYLEVEL",
# $_ts 安全框架 token(缺失则 c93l5Fya 校验失败,接口拒绝服务)
"HFZrs6pXKulzO", # 组织级 token,固定不变
"HFZrs6pXKulzP", # 请求级动态 token,服务端 Set-Cookie 持续更新
# portal 主页面会话(缺失则无法加载 pickup 页面,fetch_page_resources 失败)
"JSESSIONID",
}
OPTIONAL_COOKIE_NAMES: set[str] = {"HFZrs6pXKulzS"}
ALLOWED_COOKIE_NAMES: set[str] = REQUIRED_COOKIE_NAMES | OPTIONAL_COOKIE_NAMES
def _is_cookie_allowed(name: str) -> bool:
"""判断 cookie 是否在导入和保存的白名单中。"""
return name in ALLOWED_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。
只保存 ALLOWED_COOKIE_NAMES 中的 cookie,仅检查 REQUIRED_COOKIE_NAMES 是否齐全。
返回保存的 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_allowed(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 是否齐全(全部 9 个缺一不可)
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