2026-04-25 01:18:30 +08:00

71 lines
1.8 KiB
Go

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
}