fix: 登录过期改为跳转登录页而非返回 JSON
- Required 中间件区分页面请求(/api/ 前缀)与 SSR 页面请求,页面请求 302 跳转登录页携带 return URL - 前端 doRefresh token 刷新失败时跳转登录页,避免静默失败 - 认证页面禁止重定向避免无限循环
This commit is contained in:
@ -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) {
|
||||
|
||||
@ -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;
|
||||
|
||||
Reference in New Issue
Block a user