package auth import ( "context" "fmt" "net/http" "strings" jwt "github.com/golang-jwt/jwt/v5" ) type contextKey string const userContextKey contextKey = "gateway_user_id" // Verifier 负责在 gateway 入口层校验用户 JWT。 type Verifier struct { secret []byte } // NewVerifier 初始化入口 JWT 校验器。 func NewVerifier(secret string) *Verifier { return &Verifier{secret: []byte(secret)} } // Middleware 提取 Bearer token 并把 user_id 写入请求上下文。 func (v *Verifier) Middleware(next http.Handler) http.Handler { return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { header := request.Header.Get("Authorization") if !strings.HasPrefix(header, "Bearer ") { http.Error(writer, "missing bearer token", http.StatusUnauthorized) return } tokenString := strings.TrimPrefix(header, "Bearer ") token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) { if token.Method.Alg() != jwt.SigningMethodHS256.Alg() { return nil, fmt.Errorf("unexpected signing method") } return v.secret, nil }) if err != nil || !token.Valid { http.Error(writer, "invalid token", http.StatusUnauthorized) return } claims, ok := token.Claims.(jwt.MapClaims) if !ok { http.Error(writer, "invalid claims", http.StatusUnauthorized) return } rawUserID, ok := claims["user_id"].(float64) if !ok { http.Error(writer, "missing user_id", http.StatusUnauthorized) return } ctx := context.WithValue(request.Context(), userContextKey, int64(rawUserID)) next.ServeHTTP(writer, request.WithContext(ctx)) }) } // UserIDFromContext 提取鉴权后的用户 ID。 func UserIDFromContext(ctx context.Context) int64 { userID, _ := ctx.Value(userContextKey).(int64) return userID }