diff --git a/manual_trading_hub/hub.py b/manual_trading_hub/hub.py index dad4d30..24014a8 100644 --- a/manual_trading_hub/hub.py +++ b/manual_trading_hub/hub.py @@ -32,6 +32,7 @@ from settings_store import ( from hub_web_auth import ( SESSION_COOKIE, SESSION_MAX_AGE_SEC, + clear_session_cookie, cookie_secure_for_request, create_session_token, embed_allowed, @@ -211,8 +212,9 @@ def api_auth_login(body: LoginBody, request: Request): if not verify_credentials(body.username, body.password): raise HTTPException(status_code=401, detail="用户名或密码错误") token = create_session_token(body.username) - resp = JSONResponse({"ok": True, "session_token": token}) - set_session_cookie(resp, request, token) + embed = (request.headers.get("x-hub-embed") or "").strip() == "1" + resp = JSONResponse({"ok": True, "session_token": token, "embed": embed}) + set_session_cookie(resp, request, token, embed=embed) return resp @@ -231,15 +233,15 @@ def embed_auth_login(request: Request, token: str = "", next: str = "/monitor"): q = urlencode({"next": dest, "embed": "1"}) return RedirectResponse(f"/login?{q}", status_code=302) resp = RedirectResponse(dest, status_code=302) - set_session_cookie(resp, request, token) + set_session_cookie(resp, request, token, embed=True) return resp @app.post("/api/auth/logout") def api_auth_logout(request: Request): - secure = cookie_secure_for_request(request) + embed = (request.headers.get("x-hub-embed") or "").strip() == "1" resp = JSONResponse({"ok": True}) - resp.delete_cookie(SESSION_COOKIE, path="/", secure=secure) + clear_session_cookie(resp, request, embed=embed) return resp diff --git a/manual_trading_hub/hub_web_auth.py b/manual_trading_hub/hub_web_auth.py index c14d147..542149c 100644 --- a/manual_trading_hub/hub_web_auth.py +++ b/manual_trading_hub/hub_web_auth.py @@ -137,19 +137,40 @@ def embed_frame_ancestors() -> str: return " ".join(origins) if origins else "*" -def set_session_cookie(response, request, token: str) -> None: +def set_session_cookie(response, request, token: str, *, embed: bool = False) -> None: + """ + embed=True:LocalNav 等跨站 iframe 嵌入时须 SameSite=None + Secure(仅 HTTPS 有效)。 + """ secure = cookie_secure_for_request(request) + samesite = "lax" + if embed: + secure = True + samesite = "none" response.set_cookie( SESSION_COOKIE, token, httponly=True, - samesite="lax", + samesite=samesite, path="/", max_age=SESSION_MAX_AGE_SEC, secure=secure, ) +def clear_session_cookie(response, request, *, embed: bool = False) -> None: + secure = cookie_secure_for_request(request) + samesite = "lax" + if embed: + secure = True + samesite = "none" + response.delete_cookie( + SESSION_COOKIE, + path="/", + secure=secure, + samesite=samesite, + ) + + def is_public_path(path: str, method: str) -> bool: p = (path or "").split("?")[0].rstrip("/") or "/" if p.startswith("/assets"): diff --git a/manual_trading_hub/static/login.html b/manual_trading_hub/static/login.html index 86685b0..61edb1e 100644 --- a/manual_trading_hub/static/login.html +++ b/manual_trading_hub/static/login.html @@ -4,11 +4,7 @@ 登录 · 复盘系统中控 - - - - - + @@ -23,29 +19,53 @@
- + +
-

在 hub .env 设置 HUB_USERNAMEHUB_PASSWORD(未设用户名时默认为 admin

+

账号在云端 hub 的 .envHUB_USERNAME / HUB_PASSWORD