From 4f212b2e580f01e3af92afe66618484de6782d5f Mon Sep 17 00:00:00 2001 From: ata <17314593+asdxzaaaaa@user.noreply.gitee.com> Date: Fri, 10 Jul 2026 10:06:47 +0000 Subject: [PATCH] Add internal/middleware/jwt.go --- internal/middleware/jwt.go | 83 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 internal/middleware/jwt.go diff --git a/internal/middleware/jwt.go b/internal/middleware/jwt.go new file mode 100644 index 0000000..88cacd0 --- /dev/null +++ b/internal/middleware/jwt.go @@ -0,0 +1,83 @@ +package middleware + +import ( + "net/http" + "strings" + + "github.com/gin-gonic/gin" + "github.com/golang-jwt/jwt/v5" +) + +// 上下文键名常量,用于在 gin.Context 中存取鉴权后的用户信息 +const ( + ContextUserID = "user_id" + ContextRole = "role" +) + +// Claims JWT 自定义声明结构,嵌入标准声明 +type Claims struct { + UserID uint `json:"user_id"` + Role string `json:"role"` + jwt.RegisteredClaims +} + +// JWTAuth JWT 鉴权中间件,校验 Bearer Token 并将用户信息写入上下文 +func JWTAuth(secret string) gin.HandlerFunc { + return func(c *gin.Context) { + authHeader := c.GetHeader("Authorization") + if authHeader == "" { + c.JSON(http.StatusUnauthorized, gin.H{"code": 1, "message": "缺少认证信息", "data": nil}) + c.Abort() + return + } + + // 提取 Bearer 中的 token 部分 + parts := strings.SplitN(authHeader, " ", 2) + if len(parts) != 2 || !strings.EqualFold(parts[0], "Bearer") { + c.JSON(http.StatusUnauthorized, gin.H{"code": 1, "message": "认证格式错误", "data": nil}) + c.Abort() + return + } + + tokenString := strings.TrimSpace(parts[1]) + + // 解析并校验 token,同时确认签名算法为 HMAC + claims := &Claims{} + token, err := jwt.ParseWithClaims(tokenString, claims, func(t *jwt.Token) (interface{}, error) { + if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, jwt.ErrTokenSignatureInvalid + } + return []byte(secret), nil + }) + if err != nil || !token.Valid { + c.JSON(http.StatusUnauthorized, gin.H{"code": 1, "message": "无效或过期的令牌", "data": nil}) + c.Abort() + return + } + + // 鉴权成功,将用户信息写入上下文供后续 handler 使用 + c.Set(ContextUserID, claims.UserID) + c.Set(ContextRole, claims.Role) + c.Next() + } +} + +// GetUserID 从上下文获取用户 ID,未取到时返回 0 +func GetUserID(c *gin.Context) uint { + if v, exists := c.Get(ContextUserID); exists { + if uid, ok := v.(uint); ok { + return uid + } + } + return 0 +} + +// GetRole 从上下文获取用户角色,未取到时返回空字符串 +func GetRole(c *gin.Context) string { + if v, exists := c.Get(ContextRole); exists { + if role, ok := v.(string); ok { + return role + } + } + return "" +}