Spaces:
Sleeping
Sleeping
File size: 8,021 Bytes
8e02bdb | 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 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | #!/usr/bin/env python3
"""
即梦 AI (jimeng.jianying.com) 历史 Session 强制退出工具
==========================================================
功能:
通过 Playwright headless 浏览器模拟登录,点击"设置 → 退出"按钮,
批量使指定的历史 sessionid 在服务器端失效。
适用场景:
当 sessionid 泄露或安全审计时,强制注销不再使用的历史 cookie,
防止被未授权方继续调用 API。
使用方法:
python3 scripts/logout-sessions.py <sessionid1> [sessionid2] ...
或在脚本末尾的 SESSION_IDS 列表中填写需要退出的 sessionid,
然后直接运行:
python3 scripts/logout-sessions.py
依赖安装:
pip install playwright
playwright install chromium
注意:
- 每个 sessionid 独立启动浏览器实例执行退出,互不影响
- 已失效的 sessionid 会自动跳过
- 退出操作不可逆,请确认 sessionid 列表无误后再执行
"""
import sys
import time
import argparse
# -------------------------------------------------------
# 在此填写需要强制退出的历史 sessionid 列表
# 也可通过命令行参数传入(见使用方法)
# -------------------------------------------------------
SESSION_IDS = [
# 示例(已失效,仅作格式参考):
# "aabbddddddddddddddd",
]
def logout_session(session_id: str) -> str:
"""
对单个 sessionid 执行退出操作。
返回值:
"success" - 退出成功
"already_invalid"- sessionid 已失效,无需处理
"error_no_button"- 找不到退出按钮
"unknown" - 退出状态不确定
"error" - 发生异常
"""
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(
headless=True,
args=[
"--no-sandbox",
"--disable-setuid-sandbox",
"--disable-dev-shm-usage",
],
)
context = browser.new_context(
viewport={"width": 1920, "height": 1080},
user_agent=(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/135.0.0.0 Safari/537.36"
),
)
domain = ".jianying.com"
context.add_cookies(
[
{"name": "_tea_web_id", "value": "7619975442964235802", "domain": domain, "path": "/"},
{"name": "is_staff_user", "value": "false", "domain": domain, "path": "/"},
{"name": "store-region", "value": "cn-gd", "domain": domain, "path": "/"},
{"name": "uid_tt", "value": session_id, "domain": domain, "path": "/"},
{"name": "uid_tt_ss", "value": session_id, "domain": domain, "path": "/"},
{"name": "sid_tt", "value": session_id, "domain": domain, "path": "/"},
{"name": "sessionid", "value": session_id, "domain": domain, "path": "/"},
{"name": "sessionid_ss", "value": session_id, "domain": domain, "path": "/"},
]
)
page = context.new_page()
try:
# 导航到即梦主页,等待页面完全加载
page.goto(
"https://jimeng.jianying.com",
timeout=30000,
wait_until="networkidle",
)
time.sleep(3)
# 检查是否已登录(通过全局变量 window.__isLogined)
is_logged_in = page.evaluate("() => window.__isLogined === true")
if not is_logged_in:
return "already_invalid"
# 步骤 1:点击左侧底部设置按钮(#SiderMenuSetting)
# 该按钮点击后会弹出下拉菜单,菜单中包含"退出"选项
setting_el = page.wait_for_selector("#SiderMenuSetting", timeout=8000)
setting_el.click()
time.sleep(2)
# 步骤 2:查找弹出菜单中的"退出"按钮并点击
# 优先使用文字 selector,备选使用 class 包含文字方式
exit_btn = page.query_selector("text=退出")
if not exit_btn:
exit_btn = page.query_selector('.lv-dropdown-menu-item:has-text("退出")')
if not exit_btn:
return "error_no_button"
# 监听退出请求(可用于日志记录)
logout_requests: list[str] = []
def on_request(req):
if any(k in req.url for k in ["logout", "sign_out", "revoke", "signout"]):
logout_requests.append(req.url)
page.on("request", on_request)
# 执行点击
exit_btn.click()
time.sleep(4)
# 验证退出结果
after_state = page.evaluate("() => window.__isLogined")
current_url = page.url
if after_state is False or "login" in current_url:
return "success"
# 再次确认:检查页面是否有登录入口文字
page_content = page.content()
if "登录" in page_content[:1000]:
return "success"
return "unknown"
except Exception as e:
print(f" [异常] {e}")
return "error"
finally:
browser.close()
def main():
parser = argparse.ArgumentParser(
description="即梦 AI 历史 Session 强制退出工具",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=__doc__,
)
parser.add_argument(
"sessions",
nargs="*",
help="需要退出的 sessionid(可传多个,空格分隔)",
)
args = parser.parse_args()
# 命令行参数优先,否则使用脚本顶部的 SESSION_IDS 列表
targets = args.sessions if args.sessions else SESSION_IDS
if not targets:
parser.print_help()
print(
"\n❌ 错误:未指定任何 sessionid。\n"
" 请通过命令行传入或在脚本顶部的 SESSION_IDS 列表中填写。"
)
sys.exit(1)
# 去重并过滤空值
targets = [s.strip() for s in targets if s.strip()]
targets = list(dict.fromkeys(targets)) # 保序去重
print(f"\n即梦 AI 历史 Session 强制退出工具")
print(f"{'=' * 60}")
print(f"共 {len(targets)} 个 sessionid 待处理\n")
STATUS_ICONS = {
"success": "✅ 退出成功(服务器端已失效)",
"already_invalid":"⬜ 已失效,无需处理",
"error_no_button":"❌ 找不到退出按钮(页面结构可能已变更)",
"unknown": "⚠️ 退出状态不确定,请手动验证",
"error": "❌ 发生异常,请查看上方错误信息",
}
results: dict[str, str] = {}
for idx, sid in enumerate(targets, 1):
print(f"[{idx}/{len(targets)}] 处理: {sid}")
result = logout_session(sid)
results[sid] = result
icon = STATUS_ICONS.get(result, result)
print(f" → {icon}\n")
time.sleep(1) # 各次请求间隔,避免触发频率限制
# 汇总报告
print(f"\n{'=' * 60}")
print("退出结果汇总:")
print(f"{'=' * 60}")
for sid, result in results.items():
icon = STATUS_ICONS.get(result, result)
print(f" {sid} → {icon}")
success_count = sum(1 for r in results.values() if r == "success")
invalid_count = sum(1 for r in results.values() if r == "already_invalid")
fail_count = len(results) - success_count - invalid_count
print(f"\n ✅ 成功退出:{success_count} 个")
print(f" ⬜ 已失效: {invalid_count} 个(无需处理)")
if fail_count > 0:
print(f" ❌ 处理失败:{fail_count} 个(请手动处理)")
print()
if __name__ == "__main__":
main()
|