package jwtmw import ( "net/http" "strings" "github.com/golang-jwt/jwt/v4" ) type Claims struct { TenantID int64 `json:"tenant_id"` UserID int64 `json:"user_id"` Role string `json:"role"` jwt.RegisteredClaims } // Middleware 网关侧预检 JWT;公开路径放行。中台仍会再次校验。 func Middleware(secret string, publicPrefixes []string) func(http.HandlerFunc) http.HandlerFunc { parser := jwt.NewParser(jwt.WithValidMethods([]string{"HS256"})) return func(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { path := r.URL.Path for _, p := range publicPrefixes { if path == p || strings.HasPrefix(path, p) { next(w, r) return } } if r.Method == http.MethodOptions { next(w, r) return } auth := r.Header.Get("Authorization") if !strings.HasPrefix(strings.ToLower(auth), "bearer ") { writeErr(w, http.StatusUnauthorized, "gateway: missing bearer token") return } raw := strings.TrimSpace(auth[len("Bearer "):]) if raw == "" || strings.Count(raw, ".") != 2 { writeErr(w, http.StatusUnauthorized, "gateway: malformed token") return } token, err := parser.ParseWithClaims(raw, &Claims{}, func(t *jwt.Token) (any, error) { return []byte(secret), nil }) if err != nil || token == nil || !token.Valid { writeErr(w, http.StatusUnauthorized, "gateway: invalid token ("+shortJWTErr(err)+"),请重新登录") return } next(w, r) } } } func shortJWTErr(err error) string { if err == nil { return "rejected" } msg := err.Error() switch { case strings.Contains(msg, "expired"): return "expired" case strings.Contains(msg, "signature"): return "bad signature" case strings.Contains(msg, "malformed"): return "malformed" case strings.Contains(msg, "used before"): return "not yet valid" default: if len(msg) > 80 { return msg[:80] } return msg } } func writeErr(w http.ResponseWriter, code int, msg string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(code) _, _ = w.Write([]byte(`{"code":` + itoa(code) + `,"message":"` + escapeJSON(msg) + `"}`)) } func escapeJSON(s string) string { s = strings.ReplaceAll(s, `\`, `\\`) s = strings.ReplaceAll(s, `"`, `\"`) s = strings.ReplaceAll(s, "\n", " ") return s } func itoa(n int) string { if n == 0 { return "0" } var b [12]byte i := len(b) for n > 0 { i-- b[i] = byte('0' + n%10) n /= 10 } return string(b[i:]) }