fix: WebSocket 鉴权 — Cookie 优先 (支持 HttpOnly)

根因: panel_token 是 HttpOnly cookie, JS getCookie() 读不到
→ 前端 WS ?token= 参数为空 → 认证失败 → 仪表盘无数据

修复:
- _ws_auth_wrapper: Cookie 优先 → query token 备用 → API Key
- 浏览器同源 WS 自动发送 Cookie, 无需 ?token=
- dashboard.js / logs.js 移除多余的 getCookie+?token=

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
qinglong
2026-06-13 17:45:58 +08:00
parent 557135c376
commit 5e06c17b52
7 changed files with 84 additions and 9 deletions
+13 -3
View File
@@ -13,11 +13,21 @@ logger = logging.getLogger(__name__)
_sys_ws_clients: set = set()
def _ws_auth_wrapper(handler):
"""WebSocket 鉴权包装从 query string 取 token 验证"""
"""WebSocket 鉴权 — Cookie 优先(同源自动发送), query token 备用"""
async def wrapper(request):
token = request.query.get("token", "")
# 1. Cookie (浏览器同源自动发送, 支持 HttpOnly)
token = request.cookies.get("panel_token", "")
# 2. Query string token 备用 (跨域/非浏览器客户端)
if not token:
token = request.query.get("token", "")
# 3. API Key 验证
session_store = request.app.get("panel_session_store", {})
if not token or token not in session_store:
is_valid = token and token in session_store
if not is_valid and token:
from services.web_panel.routes.apikeys import validate_api_key
is_valid = validate_api_key(token) is not None
if not is_valid:
ws = web.WebSocketResponse()
await ws.prepare(request)
await ws.send_str(json.dumps({"error": "Unauthorized"}))