解决这个问题的核心是确保客户端系统时间准确,并且签名计算使用的 timestamp 与请求 URL 中携带的完全一致。
先说结论:大多数情况下是因为服务器时间不同步或签名算法实现有误,需优先校准时间并核对代码逻辑。
- 先确认:服务器系统时间是否与网络时间同步
- 先处理:重新生成签名,确保 timestamp 和 sign 对应
- 再验证:发送测试消息观察钉钉后台响应
命令速用版
如果你需要快速生成正确的签名参数,可以使用以下 Python 代码片段进行测试:
import time
import hmac
import hashlib
import urllib.parse
import base64
secret = '你的密钥'
timestamp = str(round(time.time() * 1000))
secret_enc = secret.encode('utf-8')
string_to_sign = '{}\n{}'.format(timestamp, secret)
string_to_sign_enc = string_to_sign.encode('utf-8')
hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
sign = urllib.parse.quote_plus(base64.b64encode(hmac_code))
print(f"timestamp={timestamp}&sign={sign}")
注意:实际请求时请将上述参数拼接到 webhook 地址后。
完整请求代码示例
以下是包含签名计算与消息发送的完整 Python 示例,可直接替换测试:
import time
import hmac
import hashlib
import base64
import urllib.parse
import requests
import json
webhook = 'https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN'
secret = 'YOUR_SECRET'
timestamp = str(round(time.time() * 1000))
secret_enc = secret.encode('utf-8')
string_to_sign = '{}\n{}'.format(timestamp, secret)
string_to_sign_enc = string_to_sign.encode('utf-8')
hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
sign = urllib.parse.quote_plus(base64.b64encode(hmac_code))
url = f'{webhook}×tamp={timestamp}&sign={sign}'
data = {
"msgtype": "text",
"text": {
"content": "测试消息:timestamp 验证修复验证"
}
}
response = requests.post(url, json=data)
print(response.status_code)
print(response.json())
为什么会这样
钉钉自定义机器人开启了“加签”安全设置后,请求必须携带 timestamp 和 sign 参数。服务端会校验这两个参数:
- 时间有效性:timestamp 必须是当前时间毫秒戳,且与服务端时间差不能过大(通常允许一定误差,但过大则失效)。
- 签名一致性:sign 是使用 HMAC-SHA256 算法,基于 timestamp 和 secret 计算得出的。如果代码里用的 timestamp 和 URL 里传的不一样,签名就会对不上。
公开资料中没有看到可靠的量化数据说明具体的时间容忍窗口,建议保持秒级同步。
分步处理
第一步:检查服务器时间
在 Linux 服务器上执行 date 命令,对比当前时间与标准时间。如果偏差超过几分钟,建议使用 timedatectl 或 chrony 同步时间。例如执行 sudo timedatectl set-ntp true 开启自动同步,或 sudo systemctl restart chronyd 重启服务。
第二步:核对签名代码
检查代码中生成 sign 的逻辑。确保字符串拼接格式是 timestamp + "\n" + secret。注意换行符是必需的,不能遗漏。
第三步:检查 URL 编码
生成的 sign 字符串包含特殊字符,必须经过 URL 编码(urlencode)后再拼接到请求地址中,否则传输过程中字符可能变质。
怎么验证是否生效
发送请求后,检查 HTTP 响应状态码是否为 200,且响应 body 中 errcode 为 0。如果群内收到消息且后台记录状态为成功,即表示修复成功。也可以在钉钉机器人管理后台查看“最近发送记录”,确认状态为成功。
常见坑
- 密钥复制多了空格:secret 前后不能包含空格或换行,建议打印长度核对。
- 时间单位错误:钉钉要求毫秒级时间戳,部分语言默认是秒级,需要乘以 1000。
- 重复使用旧时间戳:每次请求都应生成新的 timestamp,不要缓存旧的签名参数。
参考来源
- 钉钉开放平台 - 自定义机器人接入,URL: https://open.dingtalk.com/document/robots/custom-robot-access