diff --git a/manual_trading_hub/.env.example b/manual_trading_hub/.env.example index 27879af..80a17fa 100644 --- a/manual_trading_hub/.env.example +++ b/manual_trading_hub/.env.example @@ -25,7 +25,7 @@ HUB_TRUST_LAN=true # HUB_PASSWORD=your-strong-password-here # 会话签名密钥(建议单独随机串;未设则用用户名+密码拼接) # HUB_SESSION_SECRET=another-long-random-string -# HTTPS 反代时设为 true,Cookie 仅通过加密连接传输 +# HTTPS 反代时建议 true:仅 HTTPS 访问会带 Secure Cookie;http://内网IP:5100 仍可登录 # HUB_COOKIE_SECURE=true # 登录保持天数(默认 7) # HUB_SESSION_DAYS=7 diff --git a/manual_trading_hub/hub.py b/manual_trading_hub/hub.py index c7fd379..bc73f47 100644 --- a/manual_trading_hub/hub.py +++ b/manual_trading_hub/hub.py @@ -23,7 +23,7 @@ from settings_store import ( from hub_web_auth import ( SESSION_COOKIE, SESSION_MAX_AGE_SEC, - cookie_secure, + cookie_secure_for_request, create_session_token, is_public_path, password_required, @@ -160,12 +160,13 @@ def api_auth_status(request: Request): @app.post("/api/auth/login") -def api_auth_login(body: LoginBody): +def api_auth_login(body: LoginBody, request: Request): if not password_required(): return {"ok": True, "auth_disabled": True} if not verify_credentials(body.username, body.password): raise HTTPException(status_code=401, detail="用户名或密码错误") token = create_session_token(body.username) + secure = cookie_secure_for_request(request) resp = JSONResponse({"ok": True}) resp.set_cookie( SESSION_COOKIE, @@ -174,15 +175,16 @@ def api_auth_login(body: LoginBody): samesite="lax", path="/", max_age=SESSION_MAX_AGE_SEC, - secure=cookie_secure(), + secure=secure, ) return resp @app.post("/api/auth/logout") -def api_auth_logout(): +def api_auth_logout(request: Request): + secure = cookie_secure_for_request(request) resp = JSONResponse({"ok": True}) - resp.delete_cookie(SESSION_COOKIE, path="/") + resp.delete_cookie(SESSION_COOKIE, path="/", secure=secure) return resp diff --git a/manual_trading_hub/hub_web_auth.py b/manual_trading_hub/hub_web_auth.py index 30323c7..6d4e993 100644 --- a/manual_trading_hub/hub_web_auth.py +++ b/manual_trading_hub/hub_web_auth.py @@ -92,8 +92,30 @@ def validate_session_token(token: str | None) -> bool: return True -def cookie_secure() -> bool: - return (os.getenv("HUB_COOKIE_SECURE") or "").strip().lower() in ("1", "true", "yes", "on") +def cookie_secure_env_enabled() -> bool: + """是否在 .env 中启用「HTTPS 时带 Secure Cookie」策略。""" + return (os.getenv("HUB_COOKIE_SECURE") or "").strip().lower() in ( + "1", + "true", + "yes", + "on", + ) + + +def cookie_secure_for_request(request) -> bool: + """ + 仅在实际 HTTPS 访问时设置 Secure Cookie。 + 这样可同时支持:域名 HTTPS 反代 + 内网 http://IP:5100 登录。 + """ + if not cookie_secure_env_enabled(): + return False + proto = ( + (request.headers.get("x-forwarded-proto") or request.url.scheme or "http") + .split(",")[0] + .strip() + .lower() + ) + return proto == "https" def is_public_path(path: str, method: str) -> bool: diff --git a/manual_trading_hub/使用说明.md b/manual_trading_hub/使用说明.md index 622cb8a..4b3d13c 100644 --- a/manual_trading_hub/使用说明.md +++ b/manual_trading_hub/使用说明.md @@ -277,7 +277,8 @@ python hub.py | 无关键位块 | 该户 capabilities 无 `key` | 正常;Gate 趋势户无关键位 | | 局域网无法打开中控 | 防火墙 / `HUB_TRUST_LAN=0` | 放行端口或恢复默认信任私网 | | 打开即跳转登录 | 已设 `HUB_PASSWORD` | 正常;输入密码后 7 天内免登(可改 `HUB_SESSION_DAYS`) | -| 登录后仍 401 | Cookie 未带上 / HTTPS | HTTPS 反代设 `HUB_COOKIE_SECURE=true` | +| 域名能登录、IP:5100 不能 | `HUB_COOKIE_SECURE=true` 且用 http 访问 IP | 已改为仅 HTTPS 才发 Secure Cookie;或 IP 用 http、域名用 https 各登一次 | +| 登录后仍 401 | Cookie 未带上 | 反代需传 `X-Forwarded-Proto: https`;勿混用不同主机名的 Cookie | 手动探测实例桥接: