71 lines
1.8 KiB
Go
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
|
|
}
|