diff --git a/app/main.py b/app/main.py index da7e807..51dbc93 100644 --- a/app/main.py +++ b/app/main.py @@ -168,11 +168,16 @@ async def health_check(): # ==================== 页面路由 ==================== @app.get("/", response_class=HTMLResponse) -async def index(): - """返回主页""" - index_file = STATIC_DIR / "index.html" - if index_file.exists(): - return HTMLResponse(content=index_file.read_text(encoding='utf-8')) +async def index(request: Request): + """根据登录状态返回对应页面""" + token = request.cookies.get("token") + if token and verify_jwt_token(token): + index_file = STATIC_DIR / "index.html" + if index_file.exists(): + return HTMLResponse(content=index_file.read_text(encoding='utf-8')) + login_file = STATIC_DIR / "login.html" + if login_file.exists(): + return HTMLResponse(content=login_file.read_text(encoding='utf-8')) return HTMLResponse(content="

CapacityReport

Static files not found.

") diff --git a/docs/project_context.md b/docs/project_context.md index 6eb452c..0d81614 100644 --- a/docs/project_context.md +++ b/docs/project_context.md @@ -2,15 +2,15 @@ ## 最近更新记录 -### 2026-04-23: 鉴权改为全屏登录页模式 + 退出登录 -- **问题**: 之前的鉴权方案只在 API 层拦截 401 后弹出登录弹窗,但主页面 HTML 内容已经完整渲染并暴露给用户(未登录也能看到界面),不符合安全预期。 -- **修复方案**: - - **登录页独立化**: 将登录从弹窗模式(`loginModal`)改为独立全屏登录页面(`#loginPage`),未登录时主内容区 `#appContainer` 设为 `display:none`,只显示登录页。 - - **Token 启动校验**: `DOMContentLoaded` 时先检查 `localStorage` 中的 token,无 token 直接显示登录页;有 token 则用 `/api/config` 接口验证有效性,401 则跳回登录页。 - - **退出登录功能**: 每个页面 header 右上角新增退出按钮(`⏻` 图标,`.logout-btn` 类),事件委托统一处理,清除 token 后显示登录页。 - - **登录后免刷新**: 登录成功后如果应用模块未初始化则调用 `initApp()` 完成初始化,无需 `window.location.reload()`。 -- **涉及文件**: `static/index.html`、`static/js/app.js`、`static/css/style.css` -- **注意事项**: `showLoginModal()` 函数保留作为兼容入口(API 返回 401 时调用),内部已重定向到 `showLoginPage()`。 +### 2026-04-23: 登录页后端级别隔离 + 退出登录 +- **问题**: 之前登录页和主页面在同一个 HTML 文件中,即使前端隐藏了主内容,用户仍能通过查看源码看到完整的主页面 HTML。 +- **最终方案 — 后端级别隔离**: + - **独立 `login.html`**: 创建 `static/login.html` 作为独立登录页面,自包含样式和逻辑,不引用主程序任何 JS/CSS。 + - **后端路由鉴权**: `app/main.py` 的 `/` 路由从 cookie 中读取 `token` 并验证,有效则返回 `index.html`,无效则返回 `login.html`。**未登录时服务器根本不会返回 `index.html` 的内容**。 + - **Cookie + localStorage 双存储**: 登录成功后 token 同时存入 cookie(供后端 `/` 路由判断)和 localStorage(供前端 API 请求 Bearer header)。 + - **退出登录**: 每个页面 header 右上角有退出按钮(`⏻`),清除 cookie + localStorage 后跳转到 `/`(后端自动返回登录页)。 + - **API 401 处理**: `showLoginModal()` 函数保留,内部清除 token 后 `window.location.href = '/'` 跳转到登录页。 +- **涉及文件**: `static/login.html`(新增)、`app/main.py`、`static/index.html`、`static/js/app.js`、`static/css/style.css` ### 2026-04-22: 增加 JWT 鉴权和接口安全控制 - **问题背景**: 网管部门通报安全问题,扫描到项目存在暴露的 API 文档(/docs, /redoc, /openapi.json),并且 API 接口没有使用授权控制,要求快速增加鉴权。 diff --git a/static/css/style.css b/static/css/style.css index 7f2ce1d..a6fe665 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -123,79 +123,6 @@ body { transition: background-color var(--td-transition), color var(--td-transition); } -/* ==================== 登录页面 ==================== */ -.login-page { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - background: var(--td-bg-color-page); - z-index: 10001; -} - -.login-container { - width: 380px; - padding: 48px 40px; - background: var(--td-bg-color-container); - border-radius: var(--td-radius-large); - box-shadow: var(--td-shadow-3); - text-align: center; -} - -.login-logo { - font-size: 56px; - margin-bottom: 8px; - line-height: 1; -} - -.login-title { - font-size: 22px; - font-weight: 700; - color: var(--td-text-color-primary); - margin-bottom: 4px; - letter-spacing: 0.5px; -} - -.login-subtitle { - font-size: 14px; - color: var(--td-text-color-secondary); - margin-bottom: 32px; -} - -.login-form { - text-align: left; -} - -.login-form .form-group label { - display: block; - margin-bottom: 6px; - font-size: 13px; - font-weight: 500; - color: var(--td-text-color-primary); -} - -.login-form .form-input { - width: 100%; - height: 40px; - padding: 0 12px; - border: 1px solid var(--td-border-color); - border-radius: var(--td-radius-default); - font-size: 14px; - color: var(--td-text-color-primary); - background: var(--td-bg-color-container); - transition: all var(--td-transition); -} - -.login-form .form-input:focus { - outline: none; - border-color: var(--td-brand-color); - box-shadow: 0 0 0 2px var(--td-brand-color-focus); -} - /* ==================== 退出登录按钮 ==================== */ .logout-btn { width: 32px; diff --git a/static/index.html b/static/index.html index 0b85f35..381ceed 100644 --- a/static/index.html +++ b/static/index.html @@ -12,27 +12,7 @@ - -
-
- - - - -
-
- -