使用Auth0与GoFiber时获取解码JWT令牌的异常问题
问题:GoFiber中无法从上下文获取Auth0验证后的JWT Claims
问题背景
- 前端基于Next.js + Auth0搭建,已完成配置,可正常向后端发送accessToken
- 后端采用Golang + GoFiber,参照Auth0教程实现JWT验证中间件,令牌验证通过,但在接口中无法获取解析后的Claims
- 使用
adaptor.HTTPMiddleware将net/http风格的Auth0中间件转换为Fiber中间件 - 在接口处理函数
getAll中尝试获取*validator.ValidatedClaims时触发panic:panic: interface conversion: interface {} is nil, not *validator.ValidatedClaims - 排查发现Auth0中间件已将验证后的令牌存入net/http的Request上下文,但Fiber上下文无法直接读取该值
Auth0 JWT验证中间件代码
func EnsureValidToken(next http.Handler) http.Handler { // EnsureValidToken is a middleware that will check the validity of our JWT. err := godotenv.Load() if err != nil { log.Fatal("Error loading .env file") } issuerURL, err := url.Parse("https://" + os.Getenv("AUTH0_DOMAIN") + "/") if err != nil { log.Fatalf("Failed to parse the issuer url: %v", err) } provider := jwks.NewCachingProvider(issuerURL, 5*time.Minute) jwtValidator, err := validator.New( provider.KeyFunc, validator.RS256, issuerURL.String(), []string{os.Getenv("AUTH0_AUDIENCE")}, validator.WithCustomClaims( func() validator.CustomClaims { return &CustomClaims{} }, ), validator.WithAllowedClockSkew(time.Minute), ) if err != nil { log.Fatalf("Failed to set up the jwt validator") } errorHandler := func(w http.ResponseWriter, r *http.Request, err error) { log.Printf("Encountered error while validating JWT: %v", err) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusUnauthorized) w.Write([]byte(`{"message":"Failed to validate JWT."}`)) } middleware := jwtmiddleware.New( jwtValidator.ValidateToken, jwtmiddleware.WithErrorHandler(errorHandler), ) return middleware.CheckJWT(next) }
GoFiber主程序及接口处理代码
func main() { // 转换net/http中间件为Fiber中间件 var ensureValidToken = adaptor.HTTPMiddleware(EnsureValidToken) app := fiber.New() app.Use(cors.New()) app.Use(logger.New()) // 全局应用Auth0验证中间件 app.Use(ensureValidToken) app.Get("/api/books", getAll) app.Listen(":8080") } func getAll(c *fiber.Ctx) error { token := c.Context().Value(jwtmiddleware.ContextKey{}).(*validator.ValidatedClaims) // 此处触发panic,验证通过但上下文无法获取到Claims }
Auth0中间件存入上下文的核心代码
r = r.Clone(context.WithValue(r.Context(), ContextKey{}, validToken))
解决方案
问题根源在于Fiber上下文与net/http的Request上下文并非直接互通:adaptor.HTTPMiddleware转换中间件时会创建临时net/http Request对象,Auth0将Claims存入该临时对象的上下文,但Fiber的c.Context()是独立的标准库context.Context,无法读取临时Request上下文的值。
方式1:自定义Fiber中间件(推荐)
放弃net/http适配器,直接基于Fiber中间件模式实现Auth0验证逻辑,验证通过后将Claims存入Fiber的c.Locals():
func EnsureValidTokenFiber(c *fiber.Ctx) error { err := godotenv.Load() if err != nil { log.Fatal("Error loading .env file") } issuerURL, err := url.Parse("https://" + os.Getenv("AUTH0_DOMAIN") + "/") if err != nil { log.Fatalf("Failed to parse the issuer url: %v", err) } provider := jwks.NewCachingProvider(issuerURL, 5*time.Minute) jwtValidator, err := validator.New( provider.KeyFunc, validator.RS256, issuerURL.String(), []string{os.Getenv("AUTH0_AUDIENCE")}, validator.WithCustomClaims( func() validator.CustomClaims { return &CustomClaims{} }, ), validator.WithAllowedClockSkew(time.Minute), ) if err != nil { log.Fatalf("Failed to set up the jwt validator") } // 从Fiber请求头提取Bearer Token authHeader := c.Get("Authorization") if authHeader == "" { return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"message": "Missing Authorization header"}) } tokenStr := strings.TrimPrefix(authHeader, "Bearer ") if tokenStr == authHeader { return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"message": "Invalid Authorization header format"}) } // 验证Token validatedClaims, err := jwtValidator.ValidateToken(c.Context(), tokenStr) if err != nil { log.Printf("Encountered error while validating JWT: %v", err) return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"message": "Failed to validate JWT."}) } // 将验证后的Claims存入Fiber本地存储 c.Locals("validatedClaims", validatedClaims) return c.Next() }
在主程序中使用该自定义中间件:
func main() { app := fiber.New() app.Use(cors.New()) app.Use(logger.New()) // 使用自定义Fiber Auth0中间件 app.Use(EnsureValidTokenFiber) app.Get("/api/books", getAll) app.Listen(":8080") } func getAll(c *fiber.Ctx) error { // 从Fiber本地存储获取Claims token := c.Locals("validatedClaims").(*validator.ValidatedClaims) // 后续业务逻辑 return c.JSON(fiber.Map{"claims": token}) }
方式2:通过适配器传递上下文(不推荐)
若坚持使用net/http适配器,可在Auth0中间件中通过Fiber内部关联逻辑传递Claims,但依赖Fiber版本实现,稳定性较差:
func EnsureValidToken(next http.Handler) http.Handler { // ... 原Auth0中间件代码 ... // 包装Handler,将Claims存入Fiber上下文 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { middleware.CheckJWT(http.HandlerFunc(func(w2 http.ResponseWriter, r2 *http.Request) { // 从临时Request上下文获取Claims if claims := r2.Context().Value(jwtmiddleware.ContextKey{}); claims != nil { // 通过Fiber内部key获取Fiber上下文并存储Claims if fiberCtx, ok := r2.Context().Value(fiber.ContextKey).(*fiber.Ctx); ok { fiberCtx.Locals("validatedClaims", claims) } } next.ServeHTTP(w2, r2) })).ServeHTTP(w, r) }) }
内容的提问来源于stack exchange,提问作者airsoftFreak
相关产品推荐
相关产品推荐

