fix: 修复6个代码质量问题

P1-01: 提取重复的角色层级定义为包级常量
- 将 roleHierarchy 提取为 roleHierarchyLevels 包级变量
- 消除重复定义

P1-02: 修复伪随机数用于加权选择
- 使用 math/rand 的线程安全随机数生成器替代时间戳
- 确保加权路由的均匀分布

P1-03: 修复 FailureRate 初始化计算错误
- 将成功时的恢复因子从 0.9 改为 0.5
- 加速失败后的恢复过程

P1-04: 为 DefaultIAMService 添加并发控制
- 添加 sync.RWMutex 保护 map 操作
- 确保所有服务方法的线程安全

P1-05: 修复 IP 伪造漏洞
- 添加 TrustedProxies 配置
- 只在来自可信代理时才使用 X-Forwarded-For

P1-06: 修复限流 key 提取逻辑错误
- 从 Authorization header 中提取 Bearer token
- 避免使用完整的 header 作为限流 key
This commit is contained in:
Your Name
2026-04-03 07:58:46 +08:00
parent 88bf2478aa
commit 6924b2bafc
6 changed files with 448 additions and 28 deletions

View File

@@ -33,7 +33,7 @@ type Principal struct {
// BuildTokenAuthChain 构建认证中间件链
func BuildTokenAuthChain(cfg AuthMiddlewareConfig, next http.Handler) http.Handler {
handler := tokenAuthMiddleware(cfg)(next)
handler = queryKeyRejectMiddleware(handler, cfg.Auditor, cfg.Now)
handler = queryKeyRejectMiddleware(handler, cfg.Auditor, cfg.Now, cfg.TrustedProxies)
handler = requestIDMiddleware(handler, cfg.Now)
return handler
}
@@ -54,7 +54,7 @@ func requestIDMiddleware(next http.Handler, now func() time.Time) http.Handler {
}
// queryKeyRejectMiddleware 拒绝query key入站
func queryKeyRejectMiddleware(next http.Handler, auditor AuditEmitter, now func() time.Time) http.Handler {
func queryKeyRejectMiddleware(next http.Handler, auditor AuditEmitter, now func() time.Time, trustedProxies []string) http.Handler {
if next == nil {
return http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})
}
@@ -69,7 +69,7 @@ func queryKeyRejectMiddleware(next http.Handler, auditor AuditEmitter, now func(
RequestID: requestID,
Route: r.URL.Path,
ResultCode: CodeQueryKeyNotAllowed,
ClientIP: extractClientIP(r),
ClientIP: extractClientIP(r, trustedProxies),
CreatedAt: now(),
})
writeError(w, http.StatusUnauthorized, requestID, CodeQueryKeyNotAllowed, "query key not allowed")
@@ -105,7 +105,7 @@ func tokenAuthMiddleware(cfg AuthMiddlewareConfig) func(http.Handler) http.Handl
RequestID: requestID,
Route: r.URL.Path,
ResultCode: CodeAuthMissingBearer,
ClientIP: extractClientIP(r),
ClientIP: extractClientIP(r, cfg.TrustedProxies),
CreatedAt: cfg.Now(),
})
writeError(w, http.StatusUnauthorized, requestID, CodeAuthMissingBearer, "missing bearer token")
@@ -119,7 +119,7 @@ func tokenAuthMiddleware(cfg AuthMiddlewareConfig) func(http.Handler) http.Handl
RequestID: requestID,
Route: r.URL.Path,
ResultCode: CodeAuthInvalidToken,
ClientIP: extractClientIP(r),
ClientIP: extractClientIP(r, cfg.TrustedProxies),
CreatedAt: cfg.Now(),
})
writeError(w, http.StatusUnauthorized, requestID, CodeAuthInvalidToken, "invalid bearer token")
@@ -135,7 +135,7 @@ func tokenAuthMiddleware(cfg AuthMiddlewareConfig) func(http.Handler) http.Handl
SubjectID: claims.SubjectID,
Route: r.URL.Path,
ResultCode: CodeAuthTokenInactive,
ClientIP: extractClientIP(r),
ClientIP: extractClientIP(r, cfg.TrustedProxies),
CreatedAt: cfg.Now(),
})
writeError(w, http.StatusUnauthorized, requestID, CodeAuthTokenInactive, "token is inactive")
@@ -150,7 +150,7 @@ func tokenAuthMiddleware(cfg AuthMiddlewareConfig) func(http.Handler) http.Handl
SubjectID: claims.SubjectID,
Route: r.URL.Path,
ResultCode: CodeAuthScopeDenied,
ClientIP: extractClientIP(r),
ClientIP: extractClientIP(r, cfg.TrustedProxies),
CreatedAt: cfg.Now(),
})
writeError(w, http.StatusForbidden, requestID, CodeAuthScopeDenied, "scope denied")
@@ -174,7 +174,7 @@ func tokenAuthMiddleware(cfg AuthMiddlewareConfig) func(http.Handler) http.Handl
SubjectID: claims.SubjectID,
Route: r.URL.Path,
ResultCode: "OK",
ClientIP: extractClientIP(r),
ClientIP: extractClientIP(r, cfg.TrustedProxies),
CreatedAt: cfg.Now(),
})
next.ServeHTTP(w, r.WithContext(ctx))
@@ -297,15 +297,31 @@ func writeError(w http.ResponseWriter, status int, requestID, code, message stri
_ = json.NewEncoder(w).Encode(payload)
}
func extractClientIP(r *http.Request) string {
xForwardedFor := strings.TrimSpace(r.Header.Get("X-Forwarded-For"))
if xForwardedFor != "" {
parts := strings.Split(xForwardedFor, ",")
return strings.TrimSpace(parts[0])
}
host, _, err := net.SplitHostPort(r.RemoteAddr)
func extractClientIP(r *http.Request, trustedProxies []string) string {
// 检查请求是否来自可信代理
isFromTrustedProxy := false
remoteHost, _, err := net.SplitHostPort(r.RemoteAddr)
if err == nil {
return host
for _, proxy := range trustedProxies {
if remoteHost == proxy {
isFromTrustedProxy = true
break
}
}
}
// 只有来自可信代理的请求才使用X-Forwarded-For
if isFromTrustedProxy {
xForwardedFor := strings.TrimSpace(r.Header.Get("X-Forwarded-For"))
if xForwardedFor != "" {
parts := strings.Split(xForwardedFor, ",")
return strings.TrimSpace(parts[0])
}
}
// 否则使用RemoteAddr
if err == nil {
return remoteHost
}
return r.RemoteAddr
}