微信get校验必须用ctx.shouldbindquery解析signature、timestamp、nonce、echostr参数,按token+timestamp+nonce字典序拼接sha1,严格返回echostr且状态码200。

微信 GET 请求校验必须用 ctx.ShouldBindQuery 解析参数
微信服务器在接入验证阶段只发 GET 请求,携带 signature、timestamp、nonce、echostr 四个 query 参数。Gin 里不能用 c.PostForm 或 c.GetPostForm,它们只处理 POST body;也不能手动拼 c.Request.URL.Query() 再取值——容易漏掉 URL 编码解码问题。
正确做法是定义结构体并用 ctx.ShouldBindQuery:
type WechatVerifyReq struct {
Signature string `form:"signature"`
Timestamp string `form:"timestamp"`
Nonce string `form:"nonce"`
Echostr string `form:"echostr"`
}
func VerifyHandler(c *gin.Context) {
var req WechatVerifyReq
if err := c.ShouldBindQuery(&req); err != nil {
c.AbortWithStatus(400)
return
}
// 后续校验逻辑...
}
常见错误:直接用 c.Query("xxx") 取值,看似能拿到,但一旦微信传了空字符串或特殊字符(如含 + 或 %20),Query 返回值可能未正确解码,导致后续 sha1 拼接结果不一致,校验失败。
sha1 拼接顺序必须是 token + timestamp + nonce 字典序排序
微信文档明确要求:将开发者配置的 token、请求中的 timestamp、nonce 三个字符串按字典序(ASCII 码升序)排序后拼接,再做 sha1。不是按参数名顺序,也不是按传入顺序。
错误写法:sha1.Sum([]byte(token + timestamp + nonce)) —— 这忽略了排序,99% 情况下会失败。
正确做法:
- 把三个字符串放进切片:
[]string{token, timestamp, nonce} - 调用
sort.Strings()排序 - 用
strings.Join()拼接,不要用+=循环拼接(易错且无必要)
注意:所有字符串都必须是原始字符串,不额外 trim、不转大小写、不加空格。微信对大小写和空格敏感。
让 AI 读懂微信公众号。自研 7 阶段提取管道,穿透反爬率 99.89%,Token 消耗降低 50%–87%。支持 ChatGPT、Claude、Perplexity、Gemini 等平台无缝引用。
校验通过后必须原样返回 echostr,且不能带任何额外输出
微信校验成功与否,只看响应体是否**严格等于**请求里的 echostr 值,且 HTTP 状态码为 200。任何多余字符(比如换行、空格、JSON 包裹、HTML 标签、日志打印)都会导致失败。
典型翻车点:
- 用
c.JSON(200, gin.H{"data": req.Echostr})—— 返回的是 JSON,不是纯字符串 - 用
c.String(200, "ok")—— 返回了硬编码字符串,不是echostr - 在 handler 末尾多写了
log.Printf(...)导致 stdout 冲突(极少见,但某些部署环境会捕获 stdout 并混入响应)
安全写法只有一行:c.String(200, req.Echostr),且确保这是 handler 中最后一个执行语句。
Token 必须和服务号后台填写的完全一致,且不能动态生成
这个 token 是你在微信公众平台「基本配置」里填的纯文本字符串,Gin 代码里必须硬编码或从配置文件读取,绝不能每次请求都生成新值,也不能从环境变量拼接、不能加前缀后缀、不能 base64 编码后再比对。
容易被忽略的细节:
- 复制粘贴时是否带了不可见空格或全角字符?建议用
fmt.Printf("%q", token)打印出来核对 - 配置文件里用了双引号包裹,而微信后台填的是无引号字符串 —— 实际值就多了两个引号
- Git 提交时不小心把测试用的 token 提到了生产环境配置里,而线上又没 reload 配置
最稳妥的方式:把 token 定义为 const,上线前人工比对一次微信公众平台后台的输入框内容。










