update: 新增SESSION_COOKIE_SECURE配置
This commit is contained in:
@@ -325,6 +325,7 @@ services:
|
|||||||
# 会话配置
|
# 会话配置
|
||||||
- SESSION_EXPIRE_MINUTES=${SESSION_EXPIRE_MINUTES:-120}
|
- SESSION_EXPIRE_MINUTES=${SESSION_EXPIRE_MINUTES:-120}
|
||||||
- SESSION_REFRESH_THRESHOLD_MINUTES=${SESSION_REFRESH_THRESHOLD_MINUTES:-30}
|
- SESSION_REFRESH_THRESHOLD_MINUTES=${SESSION_REFRESH_THRESHOLD_MINUTES:-30}
|
||||||
|
- SESSION_COOKIE_SECURE=${SESSION_COOKIE_SECURE:-true}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"]
|
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 连接池(高并发优化)
|
# PostgreSQL 连接池(高并发优化)
|
||||||
DATABASE_POOL_SIZE=30
|
DATABASE_POOL_SIZE=30
|
||||||
DATABASE_MAX_OVERFLOW=20
|
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 配置
|
### 中转 API 配置
|
||||||
|
|
||||||
支持所有 OpenAI 兼容格式的中转服务:
|
支持所有 OpenAI 兼容格式的中转服务:
|
||||||
|
|||||||
@@ -92,6 +92,8 @@ LOCAL_AUTH_DISPLAY_NAME=本地管理员
|
|||||||
# ==========================================
|
# ==========================================
|
||||||
SESSION_EXPIRE_MINUTES=120
|
SESSION_EXPIRE_MINUTES=120
|
||||||
SESSION_REFRESH_THRESHOLD_MINUTES=30
|
SESSION_REFRESH_THRESHOLD_MINUTES=30
|
||||||
|
# 会话 Cookie 是否强制 Secure:留空按 DEBUG 自动判断;HTTP 部署可设为 false
|
||||||
|
# SESSION_COOKIE_SECURE=false
|
||||||
|
|
||||||
# ==========================================
|
# ==========================================
|
||||||
# SMTP 默认配置(可在系统设置中被管理员覆盖)
|
# SMTP 默认配置(可在系统设置中被管理员覆盖)
|
||||||
|
|||||||
+12
-3
@@ -245,17 +245,25 @@ def _validate_password(password: str):
|
|||||||
raise HTTPException(status_code=400, detail="密码长度至少为6个字符")
|
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):
|
def _set_login_cookies(response: Response, user_id: str):
|
||||||
"""设置登录 Cookie"""
|
"""设置登录 Cookie"""
|
||||||
max_age = settings.SESSION_EXPIRE_MINUTES * 60
|
max_age = settings.SESSION_EXPIRE_MINUTES * 60
|
||||||
session_token = create_session_token(user_id, max_age)
|
session_token = create_session_token(user_id, max_age)
|
||||||
|
cookie_secure = _is_session_cookie_secure()
|
||||||
response.set_cookie(
|
response.set_cookie(
|
||||||
key="session_token",
|
key="session_token",
|
||||||
value=session_token,
|
value=session_token,
|
||||||
max_age=max_age,
|
max_age=max_age,
|
||||||
httponly=True,
|
httponly=True,
|
||||||
samesite="lax",
|
samesite="lax",
|
||||||
secure=not settings.debug,
|
secure=cookie_secure,
|
||||||
)
|
)
|
||||||
|
|
||||||
china_now = get_china_now()
|
china_now = get_china_now()
|
||||||
@@ -268,7 +276,7 @@ def _set_login_cookies(response: Response, user_id: str):
|
|||||||
max_age=max_age,
|
max_age=max_age,
|
||||||
httponly=False,
|
httponly=False,
|
||||||
samesite="lax",
|
samesite="lax",
|
||||||
secure=not settings.debug,
|
secure=cookie_secure,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -693,7 +701,8 @@ async def _handle_callback(
|
|||||||
value="true",
|
value="true",
|
||||||
max_age=300,
|
max_age=300,
|
||||||
httponly=False,
|
httponly=False,
|
||||||
samesite="lax"
|
samesite="lax",
|
||||||
|
secure=_is_session_cookie_secure(),
|
||||||
)
|
)
|
||||||
logger.info(f"✅ [OAuth登录] 用户 {user.user_id} 首次登录,已设置 first_login 标记")
|
logger.info(f"✅ [OAuth登录] 用户 {user.user_id} 首次登录,已设置 first_login 标记")
|
||||||
|
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ class Settings(BaseSettings):
|
|||||||
SESSION_EXPIRE_MINUTES: int = 120 # 会话过期时间(分钟),默认2小时
|
SESSION_EXPIRE_MINUTES: int = 120 # 会话过期时间(分钟),默认2小时
|
||||||
SESSION_REFRESH_THRESHOLD_MINUTES: int = 30 # 会话刷新阈值(分钟),剩余时间少于此值时可刷新
|
SESSION_REFRESH_THRESHOLD_MINUTES: int = 30 # 会话刷新阈值(分钟),剩余时间少于此值时可刷新
|
||||||
SESSION_SECRET_KEY: Optional[str] = None # 会话签名密钥,生产环境必须配置为高强度随机值
|
SESSION_SECRET_KEY: Optional[str] = None # 会话签名密钥,生产环境必须配置为高强度随机值
|
||||||
|
SESSION_COOKIE_SECURE: Optional[bool] = None # 是否强制 Cookie Secure;None 时按 DEBUG 自动判断
|
||||||
|
|
||||||
# 系统 SMTP 默认配置(可被管理员系统设置覆盖)
|
# 系统 SMTP 默认配置(可被管理员系统设置覆盖)
|
||||||
SMTP_PROVIDER: str = "qq"
|
SMTP_PROVIDER: str = "qq"
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ services:
|
|||||||
# 会话配置
|
# 会话配置
|
||||||
- SESSION_EXPIRE_MINUTES=${SESSION_EXPIRE_MINUTES:-120}
|
- SESSION_EXPIRE_MINUTES=${SESSION_EXPIRE_MINUTES:-120}
|
||||||
- SESSION_REFRESH_THRESHOLD_MINUTES=${SESSION_REFRESH_THRESHOLD_MINUTES:-30}
|
- SESSION_REFRESH_THRESHOLD_MINUTES=${SESSION_REFRESH_THRESHOLD_MINUTES:-30}
|
||||||
|
- SESSION_COOKIE_SECURE=${SESSION_COOKIE_SECURE:-true}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"]
|
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"]
|
||||||
|
|||||||
Reference in New Issue
Block a user