Go中修改map副本同步改动原结构体变量的原因与解决方法
产生原因
jwt.MapClaims 的底层类型就是 map[string]interface{},而Go语言中map属于引用类型,不存在值拷贝语义。
当你通过类型断言拿到claims变量时,并没有复制decodedToken.Claims存储的全量map数据,只是获得了指向同一份底层map结构的引用。此时对claims的键值增删改操作,本质都是直接修改共享的底层数据,自然会同步反映到原decodedToken变量上。
这个特性是Go map的基础行为,和类型断言无关:哪怕是直接将一个map变量赋值给另一个变量,两者也会共享底层数据,修改任意一个都会影响另一个,示例如下:
m1 := map[string]string{"demo": "origin"} m2 := m1 m2["demo"] = "changed" // m1["demo"] 此时也会变为"changed"
不改动原
decodedToken的实现方法 核心思路是创建一份独立的claims副本,所有修改操作只在副本上执行:
- 方案1:手动拷贝map全量键值对生成新副本(最通用,适配使用MapClaims的场景)
注意:如果原claims中存在嵌套的map、slice、指针等引用类型值,上述单层拷贝只能隔离顶层键的修改;如果需要修改嵌套的引用类型内容,需要对对应嵌套值做递归深拷贝,才能完全和原数据隔离。// 先获取原claims引用 originalClaims := decodedToken.Claims.(jwt.MapClaims) // 初始化容量匹配的新空map claims := make(jwt.MapClaims, len(originalClaims)) // 遍历拷贝所有顶层键值对 for k, v := range originalClaims { claims[k] = v } // 后续所有修改操作都针对新的claims变量,不会影响原decodedToken for key := range claims { claims[key] = "modified" + key } - 方案2:解析JWT时使用自定义的值类型结构体接收Claims
如果业务场景允许,可以在解析token阶段就不使用默认的jwt.MapClaims,而是定义和claims字段匹配的结构体作为解析载体,结构体属于值类型,拷贝时会复制全量值数据,只要结构体内部没有嵌套引用类型字段,修改拷贝后的结构体就不会影响原token内的数据。
内容的提问来源于stack exchange,提问作者Ram
相关产品推荐
相关产品推荐

