直接解密encryptedData会失败,因为必须使用对应用户的、未过期且未被覆盖的session_key;该密钥具有一次性、时效性(通常30分钟)、严格绑定用户的特点,若Gin中未安全存储(如用sync.Map或Redis)、复用旧key、iv长度非16字节、session_key未base64解码或PKCS#7填充处理错误,均导致解密失败。

为什么直接解密 encryptedData 会失败
微信小程序的 encryptedData 必须用对应用户的 session_key 解密,而 session_key 本身是临时、一次性的——Gin 中如果没及时保存或复用过期的 session_key,解密必然报错 the signature is incorrect 或 crypto/aes: invalid key size。常见错误是把 session_key 存在全局变量或未加锁的 map 里,导致并发时被覆盖或读到旧值。
- 确保每次调用
code2Session接口后,立刻将session_key与openid绑定存储(如用sync.Map或 Redis) - 不要在多个请求间复用同一个
session_key,哪怕看起来是同一用户 - 解密前校验
iv长度必须为 16 字节,否则aes.NewCipher直接 panic
Gin 路由中安全接收并校验小程序登录凭证
用户前端调用 wx.login() 得到 code,再通过 wx.request 发送到你的 Gin 接口。这个 code 是一次性凭证,必须立即换 session,不能缓存或延迟处理。
- 接口应只接受 POST,
Content-Type: application/json,参数字段为code(string)、encryptedData(base64 string)、iv(base64 string) - 用
c.ShouldBindJSON(&req)解析,而非c.PostForm,避免类型混淆 - 对
code做长度校验(通常 48–50 字符),过短/过长直接拒绝 - 调用微信接口
https://api.weixin.qq.com/sns/jscode2session时,务必带上appid、secret和code,且用http.Client设置超时(建议 5s)
用 Go 标准库 AES/CBC 正确解密 encryptedData
微信要求使用 AES-128-CBC,PKCS#7 填充,且 session_key 必须先 base64 解码再作为密钥。标准库不自动处理填充,必须手动剥离。
-
sessionKey, _ := base64.StdEncoding.DecodeString(req.SessionKey)—— 注意这里不能忽略 error -
iv, _ := base64.StdEncoding.DecodeString(req.Iv),长度必须等于 16 - 解密后得到的明文首 16 字节是
pkcs7填充字节,需用bytes.TrimRight(data, "\x00")不可靠;正确做法是读最后 1 字节作为填充长度n,然后切片data[:len(data)-n] - 最终 JSON 反序列化前,先检查是否以
{开头,防止解密失败后 panic
block, _ := aes.NewCipher(sessionKey) mode := cipher.NewCBCDecrypter(block, iv) mode.CryptBlocks(data, data) padLen := int(data[len(data)-1]) return data[:len(data)-padLen]
从解密结果中提取 unionId 和 openid 的实际逻辑
解密后的数据是 JSON,但结构取决于用户是否绑定了微信开放平台账号:unionId 仅在绑定后存在,openid 才是小程序内唯一标识。别误以为 unionId 总是可用。
立即学习“go语言免费学习笔记(深入)”;
- 定义结构体时所有字段用指针(
*string),方便判断字段是否存在 - 优先用
openid作为用户主键,unionId仅作跨公众号/小程序关联用 - 若解密后 JSON 包含
watermark字段,必须校验其中appid是否与当前应用一致,否则可能是伪造数据 - 不要信任前端传来的
nickName或avatarUrl,它们可被篡改;应以解密数据为准,且仅用于展示
session_key 生命周期管理——它不是 token,不能当长期凭证用,也不该出现在日志或响应体里。



















