From 4f4816e093417670494d3d1fe393c4314037efca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=96=8C?= <259278618@qq.com> Date: Mon, 5 Jan 2026 16:28:29 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=99=90=E6=B5=81=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- middleware/circuit_breaker.go | 83 +++++++++++++++++++++++++---------- middleware/middleware.go | 44 +++++++++++++++++++ redis/keys.go | 14 +++--- 3 files changed, 108 insertions(+), 33 deletions(-) diff --git a/middleware/circuit_breaker.go b/middleware/circuit_breaker.go index 84aa185..82af81a 100644 --- a/middleware/circuit_breaker.go +++ b/middleware/circuit_breaker.go @@ -209,6 +209,8 @@ func (m *HalfOpenManager) checkHalfOpenSuccessThreshold(metrics HalfOpenMetrics, return false } + // 使用浮点数除法计算成功率,避免整数除法精度丢失问题 + // 例如: passedRequests=1, failedRequests=2, 则 successRate = 0.333... 而不是 0 successRate := float64(passedRequests) / float64(totalRequests) return successRate >= successThreshold } @@ -1106,32 +1108,74 @@ func syncCircuitBreakerStateToDistributed(ctx context.Context, resourceName, sta g.Log().Warningf(ctx, "分布式熔断状态同步失败,跳过: %s, 最后错误: %v", lockKey, lastErr) } -// CircuitBreakerHealthCheckHandler 健康检查接口 -func CircuitBreakerHealthCheckHandler(r *ghttp.Request) { - // 添加认证检查:使用JWT Token或API Key +// checkCircuitBreakerAuthToken 验证熔断器管理接口的认证Token +// 统一的认证逻辑,用于健康检查、重置等管理接口 +// 返回值: +// - true: 认证通过 +// - false: 认证失败,已发送401响应 +func checkCircuitBreakerAuthToken(r *ghttp.Request) bool { // 从Header中获取认证信息 authToken := r.Header.Get("Authorization") + + // 如果Header中没有,尝试从查询参数获取(仅用于开发/测试环境) + // 生产环境应禁用此方式,仅支持Header认证 if authToken == "" { - // 尝试从查询参数获取(仅用于开发环境) authToken = r.Get("authToken").String() } - // 简单的Token验证(生产环境应使用更严格的认证) - // 建议使用JWT或其他安全的认证机制 + // 检查Token是否为空 if authToken == "" { - g.Log().Warningf(r.GetCtx(), "熔断器健康检查被拒绝:缺少认证信息,IP=%s", r.GetClientIp()) + g.Log().Warningf(r.GetCtx(), "熔断器管理接口访问被拒绝:缺少认证Token, IP=%s, Path=%s", + r.GetClientIp(), r.URL.Path) r.Response.WriteStatusExit(401, "Unauthorized: Missing authentication token") - return + return false } - // TODO: 在这里添加真正的Token验证逻辑 - // 示例:使用JWT验证 - // claims, err := jwt.ParseWithClaims(authToken, &MyClaims{}) - // if err != nil { + // 支持Bearer Token格式 + if strings.HasPrefix(authToken, "Bearer ") { + authToken = strings.TrimPrefix(authToken, "Bearer ") + } + + // TODO: 实现完整的Token验证逻辑 + // 建议使用JWT或其他安全机制: + // + // 1. 使用gogf/gf/v2/os/gjwt进行JWT验证: + // token, err := gjwt.ParseAndVerify(authToken, []byte(secret)) + // if err != nil { + // return false + // } + // + // 2. 或使用其他JWT库(如github.com/golang-jwt/jwt/v5): + // claims := &MyClaims{} + // token, err := jwt.ParseWithClaims(authToken, claims, ...) + // + // 3. 验证Token的: + // - 签名有效性 + // - 过期时间(exp) + // - 签发者(iss) + // - 权限范围(scope/roles) + // + // 4. 从Token中提取用户/服务信息存储到context供后续使用 + + // 当前为简化实现,仅检查Token非空 + // 生产环境必须替换为真实的Token验证逻辑 + // 示例:使用validateToken函数(需在其他地方实现) + // if !validateToken(authToken) { + // g.Log().Warningf(r.GetCtx(), "熔断器管理接口访问被拒绝:无效的认证Token, IP=%s", r.GetClientIp()) // r.Response.WriteStatusExit(401, "Unauthorized: Invalid token") - // return + // return false // } + return true +} + +// CircuitBreakerHealthCheckHandler 健康检查接口 +func CircuitBreakerHealthCheckHandler(r *ghttp.Request) { + // 认证检查:使用Token验证 + if !checkCircuitBreakerAuthToken(r) { + return + } + page := r.Get("page").Int() size := r.Get("size").Int() if page < 0 { @@ -1237,20 +1281,11 @@ func batchProcessResources(r *ghttp.Request, processFunc func(resourceName strin // CircuitBreakerResetHandler 重置熔断器 func CircuitBreakerResetHandler(r *ghttp.Request) { - // 添加认证检查(与健康检查接口相同) - authToken := r.Header.Get("Authorization") - if authToken == "" { - authToken = r.Get("authToken").String() - } - - if authToken == "" { - g.Log().Warningf(r.GetCtx(), "熔断器重置被拒绝:缺少认证信息,IP=%s", r.GetClientIp()) - r.Response.WriteStatusExit(401, "Unauthorized: Missing authentication token") + // 认证检查 + if !checkCircuitBreakerAuthToken(r) { return } - // TODO: 添加真正的Token验证逻辑 - resourceName := r.Get("resource").String() if resourceName == "" || resourceName == "*" { diff --git a/middleware/middleware.go b/middleware/middleware.go index 9178ddc..4554720 100644 --- a/middleware/middleware.go +++ b/middleware/middleware.go @@ -30,6 +30,9 @@ func Auth(r *ghttp.Request) { } // 验证 token + // TODO: 实现完整的JWT验证逻辑 + // 当前为占位实现,实际使用时应替换为真实的token验证 + // 例如:使用gogf/gf/v2/os/gjwt或其他JWT库进行验证 if !validateToken(gstr.SubStrFrom(token, "7")) { r.Response.WriteStatusExit(401, "Unauthorized") return @@ -37,3 +40,44 @@ func Auth(r *ghttp.Request) { r.Middleware.Next() } + +// validateToken 验证Token有效性 +// 当前为简化实现,实际生产环境应使用JWT或其他安全机制进行验证 +// 示例: +// - 使用gogf/gf/v2/os/gjwt库解析和验证JWT token +// - 验证token签名、过期时间、签发者等 +// - 从token中提取用户信息并存储到context +// +// 返回值: +// - true: token有效 +// - false: token无效或过期 +func validateToken(token string) bool { + // TODO: 实现真实的token验证逻辑 + // 当前为占位实现,返回true以允许基本功能运行 + // 生产环境必须替换为真实的验证逻辑 + + // 简单的非空检查 + if token == "" { + return false + } + + // 建议的JWT验证示例(需要引入jwt库): + /* + claims := &jwt.MapClaims{} + t, err := jwt.ParseWithClaims(token, claims, func(token *jwt.Token) (interface{}, error) { + return []byte("your-secret-key"), nil + }) + if err != nil || !t.Valid { + return false + } + // 检查过期时间 + if exp, ok := (*claims)["exp"].(float64); ok { + if time.Now().Unix() > int64(exp) { + return false + } + } + */ + + // 临时返回true,实际使用时应实现完整验证 + return true +} diff --git a/redis/keys.go b/redis/keys.go index d438fe6..ac62939 100644 --- a/redis/keys.go +++ b/redis/keys.go @@ -11,13 +11,9 @@ const ( // 限流 Redis Key 常量 const ( - RateLimitKeyPrefix = "ragflow:ratelimit:" // 限流Key前缀 - RateLimitKeyIP = "ip:%s" // IP限流: ip:192.168.1.1 - RateLimitKeyUser = "user:%s" // 用户限流: user:123 或 user:anon:192.168.1.1 - RateLimitKeyService = "service:%s" // 服务限流: service:customerService - RateLimitKeyGlobal = "global:requests" // 全局限流: global:requests - RateLimitKeyOrder = "order:create:%s" // 订单创建限流: order:create:123 - RateLimitKeyTransfer = "wallet:transfer:%s" // 钱包转账限流: wallet:transfer:123 - RateLimitKeyMessage = "cs:message:%s" // 客服消息限流: cs:message:123 - RateLimitKeyUpload = "oss:upload:%s" // 文件上传限流: oss:upload:123 + RateLimitKeyPrefix = "ragflow:ratelimit:" // 限流Key前缀 + RateLimitKeyIP = "ip:%s" // IP限流: ip:192.168.1.1 + RateLimitKeyUser = "user:%s" // 用户限流: user:123 或 user:anon:192.168.1.1 + RateLimitKeyService = "service:%s" // 服务限流: service:customerService + RateLimitKeyGlobal = "global:requests" // 全局限流: global:requests )