You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go+Gin框架JWT令牌验证中间件触发500错误问题排查

Go+Gin+JWT中间件无令牌时触发500错误问题排查与修复

问题描述

跟随教程开发Go+Gin+JWT令牌验证中间件,请求携带有效JWT Cookie时中间件工作正常;但无令牌时,虽会返回401未授权,却同时引发500服务器错误,查阅JWT官方文档后未解决。

中间件代码

package middleware

import (
    "fmt"
    "net/http"
    "os"
    "time"

    "github.com/fahad-md-kamal/go-jwt/initializers"
    "github.com/fahad-md-kamal/go-jwt/models"
    "github.com/gin-gonic/gin"
    "github.com/golang-jwt/jwt/v4"
)

func RequrieAuth(c *gin.Context) {
    // 从请求中获取Cookie
    tokenString, err := c.Cookie("Authorization")
    
    if err != nil  {
        c.AbortWithStatus(http.StatusUnauthorized)
    }

    // 解码/验证令牌
    token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
        if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
            return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
        }
    
        return []byte(os.Getenv("SECRET")), nil
    })

    if token == nil {
        // fmt.Println(token.Valid)
        c.AbortWithStatus(http.StatusUnauthorized)
    }

    if claims, ok := token.Claims.(jwt.MapClaims); ok{

        if float64(time.Now().Unix()) > claims["exp"].(float64){
            c.AbortWithStatus(http.StatusUnauthorized)
        }
        // 根据令牌中的sub字段查找用户
        var user models.User
        initializers.DB.First(&user,claims["sub"])
        
        if user.ID == 0 {
            c.AbortWithStatus(http.StatusUnauthorized)
        }

        // 将用户信息附加到请求上下文
        c.Set("user", user)

        // 继续执行后续处理
        c.Next()

        } else {
        c.AbortWithStatus(http.StatusUnauthorized)
    }
}

错误信息

2022/09/30 18:50:47 [Recovery] 2022/09/30 - 18:50:47 panic recovered:
GET /validate HTTP/1.1
Host: localhost:8000
Accept: */*
Accept-Encoding: gzip, deflate, br
Cache-Control: no-cache
Connection: keep-alive
Content-Length: 102
Content-Type: application/json
Postman-Token: 9950b831-aa25-4ba3-a1eb-17c0fd8db5b4
User-Agent: PostmanRuntime/7.29.2


runtime error: invalid memory address or nil pointer dereference
/usr/local/opt/go/libexec/src/runtime/panic.go:260 (0x104c475)
        panicmem: panic(memoryError)
/usr/local/opt/go/libexec/src/runtime/signal_unix.go:835 (0x104c445)
        sigpanic: panicmem()
/Users/genex/Desktop/golang/jwt/middleware/requireAuth.go:37 (0x168016a)
        RequrieAuth: if claims, ok := token.Claims.(jwt.MapClaims); ok{
/Users/genex/golang/pkg/mod/github.com/gin-gonic/gin@v1.8.1/context.go:173 (0x166e361)
        (*Context).Next: c.handlers[c.index](c)
/Users/genex/golang/pkg/mod/github.com/gin-gonic/gin@v1.8.1/recovery.go:101 (0x166e34c)
        CustomRecoveryWithWriter.func1: c.Next()
/Users/genex/golang/pkg/mod/github.com/gin-gonic/gin@v1.8.1/context.go:173 (0x166d466)
        (*Context).Next: c.handlers[c.index](c)
/Users/genex/golang/pkg/mod/github.com/gin-gonic/gin@v1.8.1/logger.go:240 (0x166d449)
        LoggerWithConfig.func1: c.Next()
/Users/genex/golang/pkg/mod/github.com/gin-gonic/gin@v1.8.1/context.go:173 (0x166c530)
        (*Context).Next: c.handlers[c.index](c)
/Users/genex/golang/pkg/mod/github.com/gin-gonic/gin@v1.8.1/gin.go:616 (0x166c198)
        (*Engine).handleHTTPRequest: c.Next()
/Users/genex/golang/pkg/mod/github.com/gin-gonic/gin@v1.8.1/gin.go:572 (0x166bcdc)
        (*Engine).ServeHTTP: engine.handleHTTPRequest(c)
/usr/local/opt/go/libexec/src/net/http/server.go:2947 (0x12a7dab)
        serverHandler.ServeHTTP: handler.ServeHTTP(rw, req)
/usr/local/opt/go/libexec/src/net/http/server.go:1991 (0x12a2fc6)
        (*conn).serve: serverHandler{c.server}.ServeHTTP(w, w.req)
/usr/local/opt/go/libexec/src/runtime/asm_amd64.s:1594 (0x1067740)
        goexit: BYTE    $0x90   // NOP

[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 401 with 500
[GIN] 2022/09/30 - 18:50:47 | 500 |    6.815587ms |             ::1 | GET      "/validate"

问题根源与修复方案

核心问题

  1. 未终止函数执行:当获取Cookie失败(err != nil)时,调用c.AbortWithStatus(http.StatusUnauthorized)后没有执行return,导致后续代码继续运行。此时tokenString为空,jwt.Parse返回的token为nil,后续访问token.Claims触发nil指针 panic。
  2. 未处理jwt.Parse的错误:解析令牌失败时直接跳过错误检查,可能导致token无效或为nil。
  3. 未验证令牌有效性:仅检查token是否为nil,未通过token.Valid确认令牌本身合法。

修正后的中间件代码

package middleware

import (
    "fmt"
    "net/http"
    "os"
    "time"

    "github.com/fahad-md-kamal/go-jwt/initializers"
    "github.com/fahad-md-kamal/go-jwt/models"
    "github.com/gin-gonic/gin"
    "github.com/golang-jwt/jwt/v4"
)

func RequireAuth(c *gin.Context) {
    // 从请求中获取Cookie
    tokenString, err := c.Cookie("Authorization")
    
    if err != nil  {
        c.AbortWithStatus(http.StatusUnauthorized)
        return // 终止函数,避免后续代码执行
    }

    // 解码/验证令牌
    token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
        if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
            return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
        }
    
        return []byte(os.Getenv("SECRET")), nil
    })

    // 处理解析错误或无效令牌
    if err != nil || !token.Valid {
        c.AbortWithStatus(http.StatusUnauthorized)
        return
    }

    // 断言并检查Claims类型
    claims, ok := token.Claims.(jwt.MapClaims)
    if !ok {
        c.AbortWithStatus(http.StatusUnauthorized)
        return
    }

    // 检查令牌过期时间
    if float64(time.Now().Unix()) > claims["exp"].(float64) {
        c.AbortWithStatus(http.StatusUnauthorized)
        return
    }

    // 根据令牌中的sub字段查找用户
    var user models.User
    initializers.DB.First(&user, claims["sub"])
    
    if user.ID == 0 {
        c.AbortWithStatus(http.StatusUnauthorized)
        return
    }

    // 将用户信息附加到请求上下文
    c.Set("user", user)

    // 继续执行后续处理
    c.Next()
}

关键修改点说明

  • 所有c.AbortWithStatus后添加return,确保触发拦截后立即终止函数,避免后续代码执行。
  • 新增err != nil || !token.Valid检查,覆盖解析错误和令牌无效的场景。
  • 将Claims断言与分支判断拆分,逻辑更清晰,避免嵌套过深。
  • 修正函数名拼写错误(原RequrieAuth改为RequireAuth,规范命名)。

内容的提问来源于stack exchange,提问作者Fahad Md Kamal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 20:45:34