From 25017550084383107ff6dc270df9f9e706e1b57f Mon Sep 17 00:00:00 2001 From: xiamuceer Date: Wed, 29 Apr 2026 09:14:37 +0800 Subject: [PATCH] =?UTF-8?q?update:=20=E6=96=B0=E5=A2=9ESESSION=5FCOOKIE=5F?= =?UTF-8?q?SECURE=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 11 +++++++++++ backend/.env.example | 2 ++ backend/app/api/auth.py | 15 ++++++++++++--- backend/app/config.py | 1 + docker-compose.yml | 1 + 5 files changed, 27 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ffb00c7..e66c964 100644 --- a/README.md +++ b/README.md @@ -325,6 +325,7 @@ services: # 会话配置 - SESSION_EXPIRE_MINUTES=${SESSION_EXPIRE_MINUTES:-120} - SESSION_REFRESH_THRESHOLD_MINUTES=${SESSION_REFRESH_THRESHOLD_MINUTES:-30} + - SESSION_COOKIE_SECURE=${SESSION_COOKIE_SECURE:-true} restart: unless-stopped healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"] @@ -441,8 +442,18 @@ LINUXDO_REDIRECT_URI=http://localhost:8000/api/auth/callback # PostgreSQL 连接池(高并发优化) DATABASE_POOL_SIZE=30 DATABASE_MAX_OVERFLOW=20 + +# 会话 Cookie Secure 标记 +# 默认 true,适合 HTTPS 部署;如果使用 HTTP 访问并且浏览器不保存登录 Cookie,可设为 false +SESSION_COOKIE_SECURE=true ``` +> **🔐 Cookie Secure 说明** +> +> - HTTPS 部署:建议保持 `SESSION_COOKIE_SECURE=true`,浏览器只会通过 HTTPS 发送登录 Cookie。 +> - HTTP 部署:如果登录后浏览器没有保存 Cookie,请在 `.env` 中设置 `SESSION_COOKIE_SECURE=false`,然后重启后端或 Docker 容器。 +> - Docker Compose 示例默认使用 `SESSION_COOKIE_SECURE=${SESSION_COOKIE_SECURE:-true}`,如需关闭必须在 `.env` 中显式配置。 + ### 中转 API 配置 支持所有 OpenAI 兼容格式的中转服务: diff --git a/backend/.env.example b/backend/.env.example index 6935955..d6b21a8 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -92,6 +92,8 @@ LOCAL_AUTH_DISPLAY_NAME=本地管理员 # ========================================== SESSION_EXPIRE_MINUTES=120 SESSION_REFRESH_THRESHOLD_MINUTES=30 +# 会话 Cookie 是否强制 Secure:留空按 DEBUG 自动判断;HTTP 部署可设为 false +# SESSION_COOKIE_SECURE=false # ========================================== # SMTP 默认配置(可在系统设置中被管理员覆盖) diff --git a/backend/app/api/auth.py b/backend/app/api/auth.py index 6f93675..07628ae 100644 --- a/backend/app/api/auth.py +++ b/backend/app/api/auth.py @@ -245,17 +245,25 @@ def _validate_password(password: str): raise HTTPException(status_code=400, detail="密码长度至少为6个字符") +def _is_session_cookie_secure() -> bool: + """判断会话 Cookie 是否启用 Secure 标记。""" + if settings.SESSION_COOKIE_SECURE is not None: + return settings.SESSION_COOKIE_SECURE + return not settings.debug + + def _set_login_cookies(response: Response, user_id: str): """设置登录 Cookie""" max_age = settings.SESSION_EXPIRE_MINUTES * 60 session_token = create_session_token(user_id, max_age) + cookie_secure = _is_session_cookie_secure() response.set_cookie( key="session_token", value=session_token, max_age=max_age, httponly=True, samesite="lax", - secure=not settings.debug, + secure=cookie_secure, ) china_now = get_china_now() @@ -268,7 +276,7 @@ def _set_login_cookies(response: Response, user_id: str): max_age=max_age, httponly=False, samesite="lax", - secure=not settings.debug, + secure=cookie_secure, ) @@ -693,7 +701,8 @@ async def _handle_callback( value="true", max_age=300, httponly=False, - samesite="lax" + samesite="lax", + secure=_is_session_cookie_secure(), ) logger.info(f"✅ [OAuth登录] 用户 {user.user_id} 首次登录,已设置 first_login 标记") diff --git a/backend/app/config.py b/backend/app/config.py index 5d8393c..2d18703 100644 --- a/backend/app/config.py +++ b/backend/app/config.py @@ -107,6 +107,7 @@ class Settings(BaseSettings): SESSION_EXPIRE_MINUTES: int = 120 # 会话过期时间(分钟),默认2小时 SESSION_REFRESH_THRESHOLD_MINUTES: int = 30 # 会话刷新阈值(分钟),剩余时间少于此值时可刷新 SESSION_SECRET_KEY: Optional[str] = None # 会话签名密钥,生产环境必须配置为高强度随机值 + SESSION_COOKIE_SECURE: Optional[bool] = None # 是否强制 Cookie Secure;None 时按 DEBUG 自动判断 # 系统 SMTP 默认配置(可被管理员系统设置覆盖) SMTP_PROVIDER: str = "qq" diff --git a/docker-compose.yml b/docker-compose.yml index 8f871e2..96f1647 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -120,6 +120,7 @@ services: # 会话配置 - SESSION_EXPIRE_MINUTES=${SESSION_EXPIRE_MINUTES:-120} - SESSION_REFRESH_THRESHOLD_MINUTES=${SESSION_REFRESH_THRESHOLD_MINUTES:-30} + - SESSION_COOKIE_SECURE=${SESSION_COOKIE_SECURE:-true} restart: unless-stopped healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"]