// JWT鉴权:验证token并把userID存入上下文 package middleware import ( "context" "net/http" "simple-memo/global" "simple-memo/utils" "github.com/gin-gonic/gin" "go.uber.org/zap" ) func JWTAuth() gin.HandlerFunc { return func(c *gin.Context) { // 从header取token token := c.GetHeader("token") if token == "" { // 检查是否为网页端页面路由(/web/*) isWebPage := len(c.Request.URL.Path) >= 5 && c.Request.URL.Path[:5] == "/web/" if isWebPage { // 网页端页面:重定向到登录页 c.Redirect(http.StatusFound, "/login") } else { // API接口:返回JSON错误 utils.Fail(c, "请先登录") } c.Abort() return } // 检查 token 是否在黑名单中 ctx := context.Background() exists, err := global.Redis.Exists(ctx, "token_blacklist:"+token).Result() if err != nil { requestID, _ := c.Get("request_id") clientIP, _ := c.Get("client_ip") route, _ := c.Get("route") global.Logger.Warn("检查token黑名单失败", zap.String("request_id", requestID.(string)), zap.String("route", route.(string)), zap.String("client_ip", clientIP.(string)), zap.Error(err), ) } else if exists == 1 { // 检查是否为网页端页面路由 isWebPage := len(c.Request.URL.Path) >= 5 && c.Request.URL.Path[:5] == "/web/" if isWebPage { c.Redirect(http.StatusFound, "/login") } else { utils.Fail(c, "登录已过期", 1000) } c.Abort() return } // 解析 claims, err := utils.ParseToken(token) if err != nil { // 检查是否为网页端页面路由 isWebPage := len(c.Request.URL.Path) >= 5 && c.Request.URL.Path[:5] == "/web/" if isWebPage { c.Redirect(http.StatusFound, "/login") } else { utils.Fail(c, "登录已过期", 1000) } c.Abort() return } // 把userID和token存入上下文 c.Set("userID", claims.UserID) c.Set("token", token) // 同时写入标准 context,供整条链路使用 ctx = context.WithValue(c.Request.Context(), utils.UserIDKey, claims.UserID) c.Request = c.Request.WithContext(ctx) c.Next() } }