Go 语言实现钉钉机器人签名算法时,timestamp 拼接错误通常源于时间戳单位误用秒而非毫秒,或字符串拼接时遗漏换行符。排查时优先确认 time.Now().UnixMilli() 获取毫秒值,并严格按“时间戳 + 换行 + 密钥”格式生成待签名字符串。
先说结论:钉钉机器人加签验证失败多数因时间戳单位错误或签名源字符串格式不符,需严格遵循官方 HMAC-SHA256 规范。
- 先确认:时间戳必须为毫秒级 int64,而非秒级
- 先处理:待签名字符串格式为 timestamp + "\n" + secret
- 再验证:发送请求后 errcode 返回 0 即为签名生效
命令速用版
以下为 Go 语言生成签名参数的核心代码片段,可直接替换现有计算逻辑:
timestamp := strconv.FormatInt(time.Now().UnixMilli(), 10)
stringToSign := timestamp + "\n" + secret
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(stringToSign))
sign := url.QueryEscape(base64.StdEncoding.EncodeToString(mac.Sum(nil)))为什么会这样
钉钉服务端会对接收到的 timestamp 和 secret 重新计算签名,与请求中的 sign 参数比对,任何字符差异都会导致校验失败。Go 语言默认 time.Now().Unix() 返回秒级时间戳,而钉钉要求毫秒级,且字符串拼接对换行符敏感,缺少换行符会导致哈希值完全不同。
分步处理
- 获取毫秒时间戳:使用 time.Now().UnixMilli() 或 time.Now().Unix() * 1000,转换为字符串。
- 拼接待签名字符串:确保格式为 时间戳字符串 + 换行符\n + 密钥字符串,中间不能有空格。
- 计算 HMAC-SHA256:使用 crypto/hmac 和 crypto/sha256 包,密钥为 secret 内容。
- 编码与转义:先将哈希结果进行 base64 编码,再使用 url.QueryEscape 进行 URL 编码。
- 拼接请求 URL:将 timestamp 和 sign 作为查询参数附加到 webhook 地址后。
怎么验证是否生效
发送 HTTP POST 请求后,检查钉钉返回的 JSON 响应体中 errcode 字段是否为 0。若返回 310001 或类似错误码,表示签名验证失败,需重新检查 timestamp 和 sign 生成逻辑。
常见坑
- 时间戳单位错误:使用秒级时间戳会导致服务端计算结果不一致。
- 换行符遗漏:stringToSign 中 timestamp 和 secret 之间必须包含\n字符。
- 编码顺序颠倒:必须先 base64 编码再 URL 编码,顺序反了会导致特殊字符被错误转义。
- 密钥包含空格:复制 secret 时容易带入首尾空格,需手动 trim 处理。
常见问题
时间戳必须是多少位?
钉钉机器人签名要求时间戳为 13 位毫秒级整数,Go 语言中需使用 UnixMilli 方法获取。
签名计算时 secret 用在哪里?
secret 既作为 HMAC 计算的密钥 key,也作为待签名字符串的一部分参与哈希运算。
返回 errcode 310001 是什么原因?
该错误码表示签名验证失败,通常由 timestamp 过期、签名算法错误或密钥不匹配导致。
参考来源
- 钉钉开放平台 - 自定义机器人文档
- 钉钉开放平台 - 加签方式说明