03Gin中间件开发与鉴权实践

Gin中间件开发与鉴权实践

1. 中间件执行流程

[客户端请求]
    ↓
[Logger中间件] → 记录请求开始时间
    ↓
[CORS中间件] → 处理跨域请求
    ↓
[JWT鉴权] → 验证访问令牌
    ↓
[RBAC鉴权] → 校验用户权限
    ↓
[业务处理] → 核心业务逻辑
    ↓
[Logger中间件] ← 记录响应耗时

2. JWT鉴权完整实现

2.1 生成JWT令牌

func GenerateToken(userID string, roles []string) (string, error) {
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
        "userID": userID,
        "roles": roles,
        "exp": time.Now().Add(8 * time.Hour).Unix(),
    })
    return token.SignedString([]byte(os.Getenv("JWT_SECRET")))
}

2.2 鉴权中间件

func JWTAuth() gin.HandlerFunc {
    return func(c *gin.Context) {
        tokenString := strings.TrimPrefix(c.GetHeader("Authorization"), "Bearer ")
        
        token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
            if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
                return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
            }
            return []byte(os.Getenv("JWT_SECRET")), nil
        })

        if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
            c.Set("userID", claims["userID"])
            c.Set("roles", claims["roles"])
            c.Next()
        } else {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid token"})
        }
    }
}

3. RBAC权限中间件

func RequireRole(role string) gin.HandlerFunc {
    return func(c *gin.Context) {
        roles, exists := c.Get("roles")
        if !exists {
            c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "access denied"})
            return
        }

        for _, r := range roles.([]string) {
            if r == role {
                c.Next()
                return
            }
        }
        c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "insufficient permissions"})
    }
}

4. 跨域中间件配置

func CORSMiddleware() gin.HandlerFunc {
    return cors.New(cors.Config{
        AllowOrigins:     []string{"https://prod.com", "http://localhost:3000"},
        AllowMethods:     []string{"GET", "POST", "PUT", "PATCH"},
        AllowHeaders:     []string{"Origin", "Content-Type", "Authorization"},
        ExposeHeaders:    []string{"Content-Length"},
        AllowCredentials: true,
        MaxAge:           12 * time.Hour,
    })
}

5. 中间件调试技巧

  1. 上下文数据追踪
// 在中间件中添加调试信息
c.Set("requestID", uuid.NewString())
log.Printf("[%s] %s %s", c.GetString("requestID"), c.Request.Method, c.Request.URL)
  1. 中间件执行顺序验证
router.Use(
    middleware.LatencyLogger(),
    middleware.CORSMiddleware(),
    middleware.JWTAuth(),
)

6. 单元测试方案

6.1 中间件测试示例

func TestJWTMiddleware(t *testing.T) {
    router := gin.New()
    router.Use(JWTAuth())
    router.GET("/test", func(c *gin.Context) {
        c.Status(http.StatusOK)
    })

    // 有效令牌测试
    t.Run("valid token", func(t *testing.T) {
        token, _ := GenerateToken("user123", []string{"admin"})
        w := httptest.NewRecorder()
        req, _ := http.NewRequest("GET", "/test", nil)
        req.Header.Set("Authorization", "Bearer "+token)
        router.ServeHTTP(w, req)
        assert.Equal(t, http.StatusOK, w.Code)
    })
}

6.2 测试覆盖率统计

# 生成测试覆盖率报告
go test -coverprofile=coverage.out
go tool cover -html=coverage.out

最佳实践

  1. 中间件链式调用示例:
router.Use(
    middleware.Recovery(),
    middleware.CORSMiddleware(),
    middleware.JWTAuth(),
    middleware.RequireRole("admin"),
    middleware.RequestLogger(),
)
  1. 敏感信息过滤:
// 在日志中间件中过滤敏感字段
if strings.Contains(c.Request.URL.Path, "/auth") {
    c.Writer = &responseWrapper{c.Writer, c}
}
posted @ 2025-07-28 23:37  Lucas_coming  阅读(17)  评论(0)    收藏  举报