package middleware import ( "net/http" "strings" "time" "ezops/config" "ezops/model" "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v5" "golang.org/x/crypto/bcrypt" "gorm.io/gorm" ) // JWTClaims JWT声明 type JWTClaims struct { UserID uint `json:"user_id"` Username string `json:"username"` IsAdmin bool `json:"is_admin"` jwt.RegisteredClaims } // GenerateToken 生成JWT令牌 func GenerateToken(user *model.User, cfg *config.JWTConfig) (string, error) { claims := JWTClaims{ UserID: user.ID, Username: user.Username, IsAdmin: user.IsAdmin, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(cfg.ExpireHour) * time.Hour)), IssuedAt: jwt.NewNumericDate(time.Now()), Issuer: "ezops", }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString([]byte(cfg.Secret)) } // JWTAuth JWT / API AccessKey 双模式认证中间件 // Logan.Li func JWTAuth(cfg *config.JWTConfig, db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { authHeader := c.GetHeader("Authorization") if authHeader == "" { if token := c.Query("token"); token != "" { authHeader = "Bearer " + token } } // 兼容 CI/CD 脚本直接传 AccessKey 头 // Logan.Li if authHeader == "" { if key := c.GetHeader("X-AIOps-Access-Key"); key != "" { authHeader = "Bearer " + key } } if authHeader == "" { c.JSON(http.StatusUnauthorized, model.Response{Code: 401, Message: "未提供认证令牌"}) c.Abort() return } parts := strings.SplitN(authHeader, " ", 2) if !(len(parts) == 2 && parts[0] == "Bearer") { c.JSON(http.StatusUnauthorized, model.Response{Code: 401, Message: "认证令牌格式错误"}) c.Abort() return } tokenString := parts[1] if tryJWTAuth(c, cfg, tokenString) || tryAPIKeyAuth(c, db, tokenString) { c.Next() return } c.JSON(http.StatusUnauthorized, model.Response{Code: 401, Message: "无效或过期的认证令牌"}) c.Abort() } } // tryJWTAuth 尝试 JWT 认证 // Logan.Li func tryJWTAuth(c *gin.Context, cfg *config.JWTConfig, tokenString string) bool { token, err := jwt.ParseWithClaims(tokenString, &JWTClaims{}, func(token *jwt.Token) (interface{}, error) { return []byte(cfg.Secret), nil }) if err != nil { return false } claims, ok := token.Claims.(*JWTClaims) if !ok || !token.Valid { return false } c.Set("userID", claims.UserID) c.Set("username", claims.Username) c.Set("isAdmin", claims.IsAdmin) c.Set("authType", "jwt") return true } // tryAPIKeyAuth 尝试长期 API 访问密钥(prefix.secret 格式)// Logan.Li func tryAPIKeyAuth(c *gin.Context, db *gorm.DB, tokenString string) bool { if db == nil { return false } segs := strings.Split(tokenString, ".") if len(segs) != 2 || segs[0] == "" || segs[1] == "" { return false } prefix := segs[0] var user model.User if err := db.Where("api_access_key_prefix = ? AND status = 1", prefix).First(&user).Error; err != nil { return false } if user.ApiAccessKeyHash == "" { return false } if err := bcrypt.CompareHashAndPassword([]byte(user.ApiAccessKeyHash), []byte(tokenString)); err != nil { return false } // 密钥已过期则拒绝 // Logan.Li if user.ApiAccessKeyExpiresAt != nil && user.ApiAccessKeyExpiresAt.Before(time.Now()) { return false } now := time.Now() _ = db.Model(&model.User{}).Where("id = ?", user.ID).Update("api_access_key_last_used_at", now).Error c.Set("userID", user.ID) c.Set("username", user.Username) c.Set("isAdmin", user.IsAdmin) c.Set("authType", "api_key") return true } // AdminAuth 管理员认证中间件 func AdminAuth() gin.HandlerFunc { return func(c *gin.Context) { isAdmin, exists := c.Get("isAdmin") if !exists || !isAdmin.(bool) { c.JSON(http.StatusForbidden, model.Response{ Code: 403, Message: "需要管理员权限", }) c.Abort() return } c.Next() } } // PermissionAuth 校验当前用户是否拥有指定权限码,管理员直接放行。 // Logan.Li func PermissionAuth(db *gorm.DB, permissionCode string) gin.HandlerFunc { return func(c *gin.Context) { if GetIsAdmin(c) { c.Next() return } var count int64 err := db.Table("iam_user_roles ur"). Joins("JOIN iam_roles r ON r.id = ur.role_id AND r.status = 1"). Joins("JOIN iam_role_permissions rp ON rp.role_id = r.id"). Joins("JOIN iam_permissions p ON p.id = rp.permission_id AND p.status = 1"). Where("ur.user_id = ? AND p.code = ?", GetUserID(c), permissionCode). Count(&count).Error if err != nil || count == 0 { c.JSON(http.StatusForbidden, model.Response{Code: 403, Message: "无此功能权限"}) c.Abort() return } c.Next() } } // GetUserID 获取当前用户ID func GetUserID(c *gin.Context) uint { userID, exists := c.Get("userID") if !exists { return 0 } return userID.(uint) } // GetUsername 获取当前用户名 func GetUsername(c *gin.Context) string { username, exists := c.Get("username") if !exists { return "" } return username.(string) } // GetIsAdmin 获取是否为管理员 func GetIsAdmin(c *gin.Context) bool { isAdmin, exists := c.Get("isAdmin") if !exists { return false } return isAdmin.(bool) }