package authbase import ( "fmt" "net/http" "strings" "time" "git.x2erp.com/qdy/go-base/config" "git.x2erp.com/qdy/go-base/ctx" "git.x2erp.com/qdy/go-base/logger" "git.x2erp.com/qdy/go-base/util/jwt" ) // extractToken 从请求中提取令牌,优先检查Authorization头,然后检查查询参数 func extractToken(r *http.Request) (string, bool, string) { // 1. 优先检查Authorization头 authHeader := r.Header.Get("Authorization") if authHeader != "" { if logger.IsDebug() { displayHeader := authHeader if len(displayHeader) > 20 { displayHeader = displayHeader[:20] + "..." } logger.Debug("收到认证头: %s", displayHeader) } // 检查Bearer格式 if !strings.HasPrefix(authHeader, "Bearer ") { return "", false, "认证格式错误,请使用Bearer认证" } token := strings.TrimPrefix(authHeader, "Bearer ") return token, true, "" } // 2. 检查查询参数 token := r.URL.Query().Get("token") if token != "" { if logger.IsDebug() { logger.Debug("从查询参数获取token,长度: %d", len(token)) } return token, true, "" } // 3. 未找到令牌 return "", false, "缺少认证信息" } // TokenAuth 简化的Bearer认证中间件,支持从Authorization头或查询参数获取token func TokenAuth(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if logger.IsDebug() { logger.Debug("TokenAuth中间件开始处理请求: %s %s", r.Method, r.URL.Path) } // 提取令牌 token, ok, errMsg := extractToken(r) if !ok { if logger.IsDebug() { logger.Debug("提取令牌失败: %s, 请求未授权: %s %s", errMsg, r.Method, r.URL.Path) } unauthorized(w, errMsg) return } if logger.IsDebug() { logger.Debug("验证JWT令牌,令牌长度: %d", len(token)) } if logger.IsDebug() { logger.Debug("验证JWT令牌,令牌长度: %d", len(token)) } // 验证JWT令牌 claims, err := validToken(token) if err != nil { if logger.IsDebug() { logger.Debug("JWT令牌验证失败: %v", err) } unauthorized(w, fmt.Sprintf("Invalid token: %v", err)) return } if logger.IsDebug() { logger.Debug("JWT令牌验证成功,用户: %s, 租户: %s", claims.Username, claims.TenantID) } // 6. 创建请求上下文 traceID := r.Header.Get("X-Trace-ID") if traceID == "" { // 生成简单的时间戳追踪ID traceID = time.Now().Format("20060102150405.000") if logger.IsDebug() { logger.Debug("生成TraceID: %s", traceID) } } else if logger.IsDebug() { logger.Debug("使用请求中的TraceID: %s", traceID) } cfg := config.GetConfig() requestCtx := &ctx.RequestContext{ ServiceName: cfg.GetServiceConfig().ServiceName, InstanceName: cfg.GetServiceConfig().InstanceName, TraceID: traceID, TenantID: claims.TenantID, UserID: claims.UserID, Username: claims.Username, ProjectID: claims.ProjectID, } if logger.IsDebug() { logger.Debug("创建请求上下文: service=%s, instance=%s, tenant=%s, user=%s", requestCtx.ServiceName, requestCtx.InstanceName, requestCtx.TenantID, requestCtx.UserID) } // 7. 保存到请求 r = ctx.SaveContext(r, requestCtx) // 8. 继续处理 if logger.IsDebug() { logger.Debug("TokenAuth中间件处理完成,继续下一个处理器") } next.ServeHTTP(w, r) }) } // 验证令牌(需要根据实际项目实现) func validToken(token string) (*jwt.Claims, error) { secretKey := config.GetServiceConfig().SecretKey if logger.IsDebug() { logger.Debug("验证JWT令牌,secretKey长度: %d", len(secretKey)) } return jwt.ParseToken(token, secretKey) } // PrintAllHeaders 打印HTTP请求的所有头部信息 // func printAllHeaders(r *http.Request) { // if !logger.IsDebug() { // return // } // logger.Debug("=== HTTP 请求头 ===") // for name, values := range r.Header { // for _, value := range values { // logger.Debug("%s: %s", name, value) // } // } // logger.Debug("==================") // } // DebugPrintFullRequest 打印完整的HTTP请求信息(头+体),并确保请求体可被后续代码正常读取。 // 将此函数在处理请求的最开始调用。 // func debugPrintFullRequest(r *http.Request) { // if !logger.IsDebug() { // return // } // logger.Debug("🔍 [DEBUG] ====== 开始打印完整请求 ======") // // 1. 打印基本请求信息 // logger.Debug("[DEBUG] 方法: %s, URL: %s, 协议: %s", r.Method, r.URL.String(), r.Proto) // // 2. 打印所有请求头 // logger.Debug("[DEBUG] --- 请求头 ---") // // 按字母顺序排序输出,更易读 // var headers []string // for key := range r.Header { // headers = append(headers, key) // } // sort.Strings(headers) // for _, key := range headers { // logger.Debug("[DEBUG] %s: %s", key, strings.Join(r.Header[key], ", ")) // } // // 3. 【核心】读取、打印并恢复请求体 // logger.Debug("[DEBUG] --- 请求体 (原始) ---") // if r.Body == nil || r.ContentLength == 0 { // logger.Debug("[DEBUG] 请求体为空") // } else { // // 读取原始Body内容 // originalBodyBytes, err := io.ReadAll(r.Body) // if err != nil { // logger.Debug("[DEBUG] 读取请求体失败: %v", err) // // 即使失败,也要尝试恢复一个空Body,避免后续panic // r.Body = io.NopCloser(bytes.NewBuffer(nil)) // return // } // // 重要:立即关闭原Body流 // r.Body.Close() // // 打印原始内容 // logger.Debug("[DEBUG] 内容长度: %d 字节", len(originalBodyBytes)) // logger.Debug(string(originalBodyBytes)) // // 【关键步骤】将读取的字节重新设置回 r.Body,供后续使用 // r.Body = io.NopCloser(bytes.NewBuffer(originalBodyBytes)) // // 4. (可选)尝试美化打印JSON,便于阅读 // logger.Debug("[DEBUG] --- 请求体 (JSON美化) ---") // if len(originalBodyBytes) > 0 { // var prettyJSON bytes.Buffer // if err := json.Indent(&prettyJSON, originalBodyBytes, "", " "); err == nil { // // 是有效的JSON // logger.Debug(prettyJSON.String()) // } else { // // 不是JSON或格式错误,打印原始内容 // logger.Debug("[DEBUG] 内容非JSON格式,上方已打印原始内容。") // } // } // } // logger.Debug("🔍 [DEBUG] ====== 结束打印 ======\n") // }