如何将JWT自定义Claim的Id从CheckToken中间件传递到campaign.Attack函数
解决方案:通过 Request Context 传递用户ID
我来给你一个Go生态里标准且好用的解决方案——利用*http.Request的Context来传递这类请求级别的上下文数据,完全不用修改http.HandlerFunc的签名,完美适配你的场景。
下面是具体的修改步骤:
1. 定义自定义Context Key(避免命名冲突)
首先在token.go里定义一个私有类型作为Context的Key,这样能避免和其他包的key产生命名冲突,是Go里的最佳实践:
package token import ( "context" "strings" "log" "net/http" "github.com/dgrijalva/jwt-go" ) // 定义私有类型作为Context的Key,防止和其他包的key冲突 type contextKey string const userIdKey contextKey = "userId" type MyCustomClaims struct { Id int `json:"id"` // 用户ID jwt.StandardClaims }
2. 修改CheckToken中间件,将用户ID存入Request Context
你之前的CheckToken返回值里的MyCustomClaims其实是多余的,中间件只需要返回http.HandlerFunc就够了。现在调整代码,在验证token成功后,把claims.Id存入请求的Context:
func CheckToken(next http.HandlerFunc) http.HandlerFunc { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { authorizationHeader := req.Header.Get("authorization") if authorizationHeader != "" { bearerToken := strings.Split(authorizationHeader, " ") if len(bearerToken) == 2 { token, err := jwt.ParseWithClaims(bearerToken[1], &MyCustomClaims{}, func(token *jwt.Token) (interface{}, error) { return []byte("magicword"), nil }) if token.Valid { if claims, ok := token.Claims.(*MyCustomClaims); ok && token.Valid { log.Println(claims.Id) // 将用户ID存入Request的Context req = req.WithContext(context.WithValue(req.Context(), userIdKey, claims.Id)) // 继续执行后续的handler next(w, req) } else { log.Println(err) http.Error(w, "无效的Token声明", http.StatusUnauthorized) } } else if ve, ok := err.(*jwt.ValidationError); ok { if ve.Errors&jwt.ValidationErrorMalformed != 0 { http.Error(w, "Token格式错误", http.StatusBadRequest) } else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 { http.Error(w, "Token已过期或尚未生效", http.StatusUnauthorized) } else { http.Error(w, "无效的Token", http.StatusUnauthorized) } } else { http.Error(w, "解析Token失败", http.StatusInternalServerError) } } else { http.Error(w, "Authorization头格式错误", http.StatusBadRequest) } } else { http.Error(w, "需要Authorization头", http.StatusUnauthorized) } }) }
我补充了一些错误处理的返回逻辑,让你的中间件更健壮,避免出现无响应的情况。
3. 在campaign.Attack中从Context读取用户ID
修改campaign.go里的Attack函数,从请求的Context中取出之前存入的用户ID即可:
package campaign import ( "log" "net/http" "你的项目模块路径/token" // 替换成你token包的实际导入路径 ) func Attack(w http.ResponseWriter, req *http.Request) { log.Println("attack") // 从Context中读取用户ID userId, ok := req.Context().Value(token.userIdKey).(int) if !ok { http.Error(w, "从Context中获取用户ID失败", http.StatusInternalServerError) return } // 现在你可以正常使用userId了 log.Printf("发起攻击的用户ID:%d", userId) // 这里写你的业务逻辑... }
记得把
你的项目模块路径/token替换成你项目中token包的实际导入路径。
4. 修正main.go中的路由注册
因为我们调整了CheckToken的返回值,不过你原来的路由注册代码完全可以正常工作,不需要修改:
func main() { router := mux.NewRouter() router.HandleFunc("/attack", token.CheckToken(campaign.Attack)).Methods("GET", "OPTIONS") log.Fatal(http.ListenAndServe(":3000", handlers.CORS(handlers.AllowedOrigins([]string{"*"}), handlers.AllowedHeaders([]string{"Content-Type", "authorization"}))(router))) }
这种方法的优势:
- 完全遵循Go HTTP标准库的设计,不需要修改Handler的签名,兼容性拉满
- Context是请求级别的,数据只会在当前请求的生命周期内存在,不会有并发安全问题
- 通过自定义的contextKey避免了命名冲突,类型更安全
内容的提问来源于stack exchange,提问作者licorpolo
相关产品推荐
相关产品推荐

