| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
- 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")
- // }
|