Нет описания
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. package authbase
  2. import (
  3. "fmt"
  4. "net/http"
  5. "strings"
  6. "time"
  7. "git.x2erp.com/qdy/go-base/config"
  8. "git.x2erp.com/qdy/go-base/ctx"
  9. "git.x2erp.com/qdy/go-base/logger"
  10. "git.x2erp.com/qdy/go-base/util/jwt"
  11. )
  12. // extractToken 从请求中提取令牌,优先检查Authorization头,然后检查查询参数
  13. func extractToken(r *http.Request) (string, bool, string) {
  14. // 1. 优先检查Authorization头
  15. authHeader := r.Header.Get("Authorization")
  16. if authHeader != "" {
  17. if logger.IsDebug() {
  18. displayHeader := authHeader
  19. if len(displayHeader) > 20 {
  20. displayHeader = displayHeader[:20] + "..."
  21. }
  22. logger.Debug("收到认证头: %s", displayHeader)
  23. }
  24. // 检查Bearer格式
  25. if !strings.HasPrefix(authHeader, "Bearer ") {
  26. return "", false, "认证格式错误,请使用Bearer认证"
  27. }
  28. token := strings.TrimPrefix(authHeader, "Bearer ")
  29. return token, true, ""
  30. }
  31. // 2. 检查查询参数
  32. token := r.URL.Query().Get("token")
  33. if token != "" {
  34. if logger.IsDebug() {
  35. logger.Debug("从查询参数获取token,长度: %d", len(token))
  36. }
  37. return token, true, ""
  38. }
  39. // 3. 未找到令牌
  40. return "", false, "缺少认证信息"
  41. }
  42. // TokenAuth 简化的Bearer认证中间件,支持从Authorization头或查询参数获取token
  43. func TokenAuth(next http.Handler) http.Handler {
  44. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  45. if logger.IsDebug() {
  46. logger.Debug("TokenAuth中间件开始处理请求: %s %s", r.Method, r.URL.Path)
  47. }
  48. // 提取令牌
  49. token, ok, errMsg := extractToken(r)
  50. if !ok {
  51. if logger.IsDebug() {
  52. logger.Debug("提取令牌失败: %s, 请求未授权: %s %s", errMsg, r.Method, r.URL.Path)
  53. }
  54. unauthorized(w, errMsg)
  55. return
  56. }
  57. if logger.IsDebug() {
  58. logger.Debug("验证JWT令牌,令牌长度: %d", len(token))
  59. }
  60. if logger.IsDebug() {
  61. logger.Debug("验证JWT令牌,令牌长度: %d", len(token))
  62. }
  63. // 验证JWT令牌
  64. claims, err := validToken(token)
  65. if err != nil {
  66. if logger.IsDebug() {
  67. logger.Debug("JWT令牌验证失败: %v", err)
  68. }
  69. unauthorized(w, fmt.Sprintf("Invalid token: %v", err))
  70. return
  71. }
  72. if logger.IsDebug() {
  73. logger.Debug("JWT令牌验证成功,用户: %s, 租户: %s", claims.Username, claims.TenantID)
  74. }
  75. // 6. 创建请求上下文
  76. traceID := r.Header.Get("X-Trace-ID")
  77. if traceID == "" {
  78. // 生成简单的时间戳追踪ID
  79. traceID = time.Now().Format("20060102150405.000")
  80. if logger.IsDebug() {
  81. logger.Debug("生成TraceID: %s", traceID)
  82. }
  83. } else if logger.IsDebug() {
  84. logger.Debug("使用请求中的TraceID: %s", traceID)
  85. }
  86. cfg := config.GetConfig()
  87. requestCtx := &ctx.RequestContext{
  88. ServiceName: cfg.GetServiceConfig().ServiceName,
  89. InstanceName: cfg.GetServiceConfig().InstanceName,
  90. TraceID: traceID,
  91. TenantID: claims.TenantID,
  92. UserID: claims.UserID,
  93. Username: claims.Username,
  94. ProjectID: claims.ProjectID,
  95. }
  96. if logger.IsDebug() {
  97. logger.Debug("创建请求上下文: service=%s, instance=%s, tenant=%s, user=%s",
  98. requestCtx.ServiceName, requestCtx.InstanceName, requestCtx.TenantID, requestCtx.UserID)
  99. }
  100. // 7. 保存到请求
  101. r = ctx.SaveContext(r, requestCtx)
  102. // 8. 继续处理
  103. if logger.IsDebug() {
  104. logger.Debug("TokenAuth中间件处理完成,继续下一个处理器")
  105. }
  106. next.ServeHTTP(w, r)
  107. })
  108. }
  109. // 验证令牌(需要根据实际项目实现)
  110. func validToken(token string) (*jwt.Claims, error) {
  111. secretKey := config.GetServiceConfig().SecretKey
  112. if logger.IsDebug() {
  113. logger.Debug("验证JWT令牌,secretKey长度: %d", len(secretKey))
  114. }
  115. return jwt.ParseToken(token, secretKey)
  116. }
  117. // PrintAllHeaders 打印HTTP请求的所有头部信息
  118. // func printAllHeaders(r *http.Request) {
  119. // if !logger.IsDebug() {
  120. // return
  121. // }
  122. // logger.Debug("=== HTTP 请求头 ===")
  123. // for name, values := range r.Header {
  124. // for _, value := range values {
  125. // logger.Debug("%s: %s", name, value)
  126. // }
  127. // }
  128. // logger.Debug("==================")
  129. // }
  130. // DebugPrintFullRequest 打印完整的HTTP请求信息(头+体),并确保请求体可被后续代码正常读取。
  131. // 将此函数在处理请求的最开始调用。
  132. // func debugPrintFullRequest(r *http.Request) {
  133. // if !logger.IsDebug() {
  134. // return
  135. // }
  136. // logger.Debug("🔍 [DEBUG] ====== 开始打印完整请求 ======")
  137. // // 1. 打印基本请求信息
  138. // logger.Debug("[DEBUG] 方法: %s, URL: %s, 协议: %s", r.Method, r.URL.String(), r.Proto)
  139. // // 2. 打印所有请求头
  140. // logger.Debug("[DEBUG] --- 请求头 ---")
  141. // // 按字母顺序排序输出,更易读
  142. // var headers []string
  143. // for key := range r.Header {
  144. // headers = append(headers, key)
  145. // }
  146. // sort.Strings(headers)
  147. // for _, key := range headers {
  148. // logger.Debug("[DEBUG] %s: %s", key, strings.Join(r.Header[key], ", "))
  149. // }
  150. // // 3. 【核心】读取、打印并恢复请求体
  151. // logger.Debug("[DEBUG] --- 请求体 (原始) ---")
  152. // if r.Body == nil || r.ContentLength == 0 {
  153. // logger.Debug("[DEBUG] 请求体为空")
  154. // } else {
  155. // // 读取原始Body内容
  156. // originalBodyBytes, err := io.ReadAll(r.Body)
  157. // if err != nil {
  158. // logger.Debug("[DEBUG] 读取请求体失败: %v", err)
  159. // // 即使失败,也要尝试恢复一个空Body,避免后续panic
  160. // r.Body = io.NopCloser(bytes.NewBuffer(nil))
  161. // return
  162. // }
  163. // // 重要:立即关闭原Body流
  164. // r.Body.Close()
  165. // // 打印原始内容
  166. // logger.Debug("[DEBUG] 内容长度: %d 字节", len(originalBodyBytes))
  167. // logger.Debug(string(originalBodyBytes))
  168. // // 【关键步骤】将读取的字节重新设置回 r.Body,供后续使用
  169. // r.Body = io.NopCloser(bytes.NewBuffer(originalBodyBytes))
  170. // // 4. (可选)尝试美化打印JSON,便于阅读
  171. // logger.Debug("[DEBUG] --- 请求体 (JSON美化) ---")
  172. // if len(originalBodyBytes) > 0 {
  173. // var prettyJSON bytes.Buffer
  174. // if err := json.Indent(&prettyJSON, originalBodyBytes, "", " "); err == nil {
  175. // // 是有效的JSON
  176. // logger.Debug(prettyJSON.String())
  177. // } else {
  178. // // 不是JSON或格式错误,打印原始内容
  179. // logger.Debug("[DEBUG] 内容非JSON格式,上方已打印原始内容。")
  180. // }
  181. // }
  182. // }
  183. // logger.Debug("🔍 [DEBUG] ====== 结束打印 ======\n")
  184. // }