From 8902af420e1956a9f81465d6f2684a2497811b4c Mon Sep 17 00:00:00 2001 From: Victor_Jay Date: Mon, 1 Jun 2026 12:19:18 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=99=BB=E5=BD=95=E8=BF=87=E6=9C=9F?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E8=B7=B3=E8=BD=AC=E7=99=BB=E5=BD=95=E9=A1=B5?= =?UTF-8?q?=E8=80=8C=E9=9D=9E=E8=BF=94=E5=9B=9E=20JSON?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Required 中间件区分页面请求(/api/ 前缀)与 SSR 页面请求,页面请求 302 跳转登录页携带 return URL - 前端 doRefresh token 刷新失败时跳转登录页,避免静默失败 - 认证页面禁止重定向避免无限循环 --- internal/middleware/auth.go | 26 ++++++++++++++-------- templates/MetaLab-2026/static/js/common.js | 11 ++++++++- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/internal/middleware/auth.go b/internal/middleware/auth.go index e07a403..217059d 100644 --- a/internal/middleware/auth.go +++ b/internal/middleware/auth.go @@ -2,6 +2,7 @@ package middleware import ( "net/http" + "strings" "metazone.cc/metalab/internal/common" "metazone.cc/metalab/internal/config" @@ -35,24 +36,19 @@ func (am *AuthMiddleware) WithLevelService(svc autoCheckIner) *AuthMiddleware { } // Required 登录认证中间件:读 session cookie → 验证会话 → 注入用户信息 -// 验证失败 → 清除 cookie → 返回 401 +// 页面请求(非 /api/ 前缀)→ 验证失败清除 cookie 并重定向到登录页 +// API 请求 → 验证失败返回 401 JSON func (am *AuthMiddleware) Required() gin.HandlerFunc { return func(c *gin.Context) { sid, err := c.Cookie(common.SessionCookieName) if err != nil || sid == "" { - common.ClearSessionCookie(c, am.cfg) - c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ - "success": false, "message": "登录已过期,请重新登录", - }) + am.abortUnauthorized(c) return } s, err := am.sessionManager.Validate(sid) if err != nil || s == nil { - common.ClearSessionCookie(c, am.cfg) - c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ - "success": false, "message": "登录已过期,请重新登录", - }) + am.abortUnauthorized(c) return } @@ -62,6 +58,18 @@ func (am *AuthMiddleware) Required() gin.HandlerFunc { } } +// abortUnauthorized 统一处理未认证:API 返回 JSON,页面请求跳转登录 +func (am *AuthMiddleware) abortUnauthorized(c *gin.Context) { + common.ClearSessionCookie(c, am.cfg) + if strings.HasPrefix(c.Request.URL.Path, "/api/") { + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ + "success": false, "message": "登录已过期,请重新登录", + }) + } else { + common.RedirectToLogin(c) + } +} + // Optional 可选认证:已登录则注入用户信息,未登录也放行 func (am *AuthMiddleware) Optional() gin.HandlerFunc { return func(c *gin.Context) { diff --git a/templates/MetaLab-2026/static/js/common.js b/templates/MetaLab-2026/static/js/common.js index a4f9b0d..3dbc8dc 100644 --- a/templates/MetaLab-2026/static/js/common.js +++ b/templates/MetaLab-2026/static/js/common.js @@ -45,6 +45,13 @@ } // ---- Token refresh logic ---- + function redirectToLogin() { + // 避免在认证页面上造成无限跳转循环 + if (/^\/auth\//.test(window.location.pathname)) return; + var returnURL = encodeURIComponent(window.location.pathname + window.location.search); + window.location.href = '/auth/login?redirect=' + returnURL; + } + function doRefresh() { if (isRefreshing) return refreshPromise; isRefreshing = true; @@ -53,11 +60,12 @@ isRefreshing = false; refreshPromise = null; if (!res.ok) { - // Refresh failed — clear all pending retries + // Refresh failed — clear all pending retries and redirect to login while (pendingRetries.length) { var r = pendingRetries.shift(); r.reject(new Error('refresh_failed')); } + redirectToLogin(); return Promise.reject(new Error('refresh_failed')); } // Retry all pending requests @@ -74,6 +82,7 @@ var r2 = pendingRetries.shift(); r2.reject(err); } + redirectToLogin(); return Promise.reject(err); }); return refreshPromise;