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

Gin上下文变量在并行REST请求中被覆盖的问题如何修复?

问题修复方案

你的问题核心是全局变量在并发请求中的竞态条件:role、userName、userId这三个全局变量被所有请求共享,当多个请求并行处理时,后一个请求的赋值会覆盖前一个请求的结果,导致上下文里的用户信息混乱——这就是问题仅在并行请求时偶发出现的原因。

具体修复步骤

1. 移除全局变量,改用局部变量

将原本定义在函数外的全局变量移到ParseJWTToken函数内部,变成局部变量。每个请求的处理goroutine都会创建自己的变量副本,完全隔离,不会互相干扰。

2. 使用Gin官方推荐的上下文操作方法

直接赋值c.Keys虽然可行,但Gin提供了c.Set()和c.Get()方法来安全操作请求上下文的键值对,避免直接操作底层map可能带来的潜在问题。

修改后的完整代码

package main

import (
    "fmt"
    "strings"

    "github.com/gin-gonic/gin"
    "github.com/golang-jwt/jwt"
)

func main() {
    g := gin.Default()
    g.Use(ParseJWTToken)
    g.GET("/hello/:name", hello)
    g.Run(":9000")
}

func hello(c *gin.Context) {
    // 使用c.Set()添加新的上下文值,而非直接覆盖c.Keys
    c.Set("current_user_id", 10)
    c.Set("current_user_name", c.Param("name"))
    
    fmt.Println(c.Keys)
    c.String(200, "Hello %s", c.Param("name"))
}

func ParseJWTToken(c *gin.Context) {
    // 所有变量改为局部变量,请求隔离
    var role, userName string
    var userId float64
    var userAgentCheck bool
    var userAgent string

    if values, ok := c.Request.Header["Authorization"]; ok && len(values) > 0 {
        bearer := strings.Split(values[0], "Bearer")
        bearerToken := strings.TrimSpace(bearer[1])

        if uaValues, ok := c.Request.Header["User-Agent"]; ok && len(uaValues) > 0 {
            userAgent = uaValues[0]
        }

        _ = config.InitKeys()
        token, err := jwt.Parse(bearerToken, func(token *jwt.Token) (interface{}, error) {
            return config.SignKey, nil
        })

        // 合并错误判断,简化逻辑
        if err != nil || !token.Valid {
            c.Abort()
            return
        }

        if claims, ok := token.Claims.(jwt.MapClaims); ok && len(claims) > 0 {
            for key, claim := range claims {
                switch key {
                case "user_agent":
                    if claim == userAgent {
                        userAgentCheck = true
                    }
                case "role":
                    role = claim.(string)
                case "id":
                    userId = claim.(float64)
                case "name":
                    userName = claim.(string)
                }
            }
        }

        // 用c.Set()设置上下文值
        c.Set("userid", userId)
        c.Set("role", role)
        c.Set("username", userName)

        if userAgentCheck {
            c.Next()
        } else {
            c.Abort()
        }
    } else {
        c.Abort()
    }
}

额外说明

  • 后续在其他handler中获取用户信息时,推荐使用c.Get()方法,例如:userId, ok := c.Get("userid"),同时记得判断ok来确保键存在。
  • 优化了原代码中的条件判断逻辑,让错误处理更简洁。

内容的提问来源于stack exchange,提问作者Amandeep kaur

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 00:25:56