From a3072ccbfb262d309032bbe905105c3ce5f0a6bb Mon Sep 17 00:00:00 2001 From: YANG JIANKUAN Date: Thu, 3 Sep 2026 17:38:38 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=AE=A1=E7=90=86=E7=95=8C=E9=9D=A2?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=20token=20=E7=99=BB=E5=BD=95=E9=97=A8?= =?UTF-8?q?=E7=A6=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 此前不填 token 也能进入各页面,只是没有内容。新增受鉴权保护的 /api/auth/session 探针与登录页,路由守卫在进入任何非登录页前先探针, 未通过则跳登录页并记住原目标;接口返回 401 时自动清 token 回登录页; 顶栏加退出按钮,撤掉设置页里的 token 卡片。本机未设 ADMIN_TOKEN 时 探针直接通过,开发体验不变。 Co-Authored-By: Claude Opus 4.6 --- server/src/index.ts | 2 + web/src/App.vue | 36 +++++++++- web/src/api/client.ts | 3 + web/src/pages/LoginPage.vue | 127 +++++++++++++++++++++++++++++++++ web/src/pages/SettingsPage.vue | 18 +---- web/src/router.ts | 15 ++++ web/src/stores/auth.ts | 62 ++++++++++++++++ 7 files changed, 244 insertions(+), 19 deletions(-) create mode 100644 web/src/pages/LoginPage.vue create mode 100644 web/src/stores/auth.ts diff --git a/server/src/index.ts b/server/src/index.ts index 778e8bb..003f59d 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -22,6 +22,8 @@ const app = new Hono(); app.get('/api/health', (c) => c.json({ ok: true, name: 'proxy-station' })); app.use('/api/*', adminAuth); +// 鉴权探针:能到这里说明已通过 adminAuth(token 正确,或未设 token 且来源为本机/局域网) +app.get('/api/auth/session', (c) => c.json({ ok: true, tokenRequired: !!env.adminToken })); app.route('/api/nodes', nodesRoute); app.route('/api/groups', groupsRoute); app.route('/api/rules', rulesRoute); diff --git a/web/src/App.vue b/web/src/App.vue index f0eeee4..104c9a9 100644 --- a/web/src/App.vue +++ b/web/src/App.vue @@ -3,9 +3,23 @@ import { computed } from 'vue'; import { useRoute, useRouter } from 'vue-router'; import { NConfigProvider, NMessageProvider, NDialogProvider } from 'naive-ui'; import { themeOverrides } from './theme'; +import { useAuthStore } from './stores/auth'; +import { UNAUTHORIZED_EVENT } from './api/client'; const route = useRoute(); const router = useRouter(); +const auth = useAuthStore(); + +// 任何接口回 401:本地 token 已失效,清掉并回登录页 +window.addEventListener(UNAUTHORIZED_EVENT, () => { + auth.invalidate(); + if (!route.meta.public) router.replace({ path: '/login', query: { redirect: route.fullPath } }); +}); + +function logout() { + auth.logout(); + router.replace('/login'); +} const navItems = [ { path: '/nodes', label: '节点' }, @@ -22,7 +36,8 @@ const active = computed(() => route.path); -
+ +
@@ -43,6 +58,7 @@ const active = computed(() => route.path);
直连出口 WARP 出口 +
@@ -156,6 +172,22 @@ const active = computed(() => route.path); white-space: nowrap; } +.logout { + appearance: none; + border: 1px solid var(--line); + background: transparent; + color: var(--muted); + font: 600 12px var(--font-body); + padding: 4px 12px; + border-radius: 999px; + cursor: pointer; +} + +.logout:hover { + color: var(--text); + border-color: var(--text); +} + .content { max-width: 1240px; margin: 0 auto; @@ -178,7 +210,7 @@ const active = computed(() => route.path); overflow-x: auto; } - .legend { + .legend-item { display: none; } diff --git a/web/src/api/client.ts b/web/src/api/client.ts index 3f71020..69740a8 100644 --- a/web/src/api/client.ts +++ b/web/src/api/client.ts @@ -1,4 +1,5 @@ const ADMIN_TOKEN_KEY = 'proxy-station:admin-token'; +export const UNAUTHORIZED_EVENT = 'proxy-station:unauthorized'; export function getAdminToken(): string { return localStorage.getItem(ADMIN_TOKEN_KEY) || ''; @@ -30,6 +31,8 @@ async function request(method: string, path: string, body?: unknown): Promise }); const data = await res.json().catch(() => null); if (!res.ok) { + // token 失效或被更换:广播给路由层回登录页。用事件而不是直接 import router,避免 client → router → store → client 的循环依赖 + if (res.status === 401 && !path.startsWith('/api/auth/')) window.dispatchEvent(new CustomEvent(UNAUTHORIZED_EVENT)); throw new ApiError(res.status, (data as any)?.error || `请求失败(${res.status})`); } return data as T; diff --git a/web/src/pages/LoginPage.vue b/web/src/pages/LoginPage.vue new file mode 100644 index 0000000..fa2ad2d --- /dev/null +++ b/web/src/pages/LoginPage.vue @@ -0,0 +1,127 @@ + + + + + diff --git a/web/src/pages/SettingsPage.vue b/web/src/pages/SettingsPage.vue index 821c05d..320bb8d 100644 --- a/web/src/pages/SettingsPage.vue +++ b/web/src/pages/SettingsPage.vue @@ -1,14 +1,12 @@