package middleware import ( "context" "errors" "log/slog" "strings" "git.lzcwy.cn/zhengchiliu/zt-online-game-backend/pkg/app" "git.lzcwy.cn/zhengchiliu/zt-online-game-backend/pkg/errcode" "git.lzcwy.cn/zhengchiliu/zt-online-game-backend/pkg/lzcwyjwt" "github.com/gin-gonic/gin" ) type ctxKey string const ( userIDKey ctxKey = "userID" ) func JWTAuth() gin.HandlerFunc { return func(c *gin.Context) { response := app.NewResponse(c) // 1. 从 Authorization 头取 token:Bearer xxx.yyy.zzz authHeader := c.GetHeader("Authorization") if authHeader == "" { slog.WarnContext(c.Request.Context(), "jwt auth: authorization header missing", "method", c.Request.Method, "path", c.Request.URL.Path, "ip", c.ClientIP()) response.ToErrorResponse(errcode.UnauthorizedTokenMissing) c.Abort() return } parts := strings.SplitN(authHeader, " ", 2) if len(parts) != 2 || parts[0] != "Bearer" { slog.WarnContext(c.Request.Context(), "jwt auth: invalid authorization format", "method", c.Request.Method, "path", c.Request.URL.Path, "ip", c.ClientIP()) response.ToErrorResponse(errcode.UnauthorizedTokenError.WithDetails("invalid Authorization format")) c.Abort() return } // 2. 解析 token claims, err := lzcwyjwt.ParseAccessToken(parts[1]) if err != nil { switch { case errors.Is(err, lzcwyjwt.ErrTokenExpired): slog.InfoContext(c.Request.Context(), "jwt auth: token expired", "path", c.Request.URL.Path) response.ToErrorResponse(errcode.UnauthorizedTokenTimeout) case errors.Is(err, lzcwyjwt.ErrTokenWrongType): slog.WarnContext(c.Request.Context(), "jwt auth: token type mismatch", "path", c.Request.URL.Path) response.ToErrorResponse(errcode.UnauthorizedTokenError.WithDetails("token type mismatch")) default: slog.WarnContext(c.Request.Context(), "jwt auth: parse token failed", "path", c.Request.URL.Path, "ip", c.ClientIP(), "error", err) response.ToErrorResponse(errcode.UnauthorizedTokenError.WithDetails(err.Error())) } c.Abort() return } // 4. 把 userID注入 context ctx := context.WithValue(c.Request.Context(), userIDKey, claims.UserID) c.Request = c.Request.WithContext(ctx) c.Next() } } // GetUserID 从 context 取登录用户 ID(在 handler 里用) func GetUserID(ctx context.Context) (int64, bool) { uid, ok := ctx.Value(userIDKey).(int64) return uid, ok }