Files
ai_site/gateway/internal/jwtmw/jwt.go
2026-07-31 10:31:17 +08:00

103 lines
2.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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:])
}