加签模式适合对安全性有要求的生产环境,需要在发送请求前由服务端计算签名并拼接到 URL 中。
核心结论:加签模式比关键词模式更安全,但必须确保服务端时间准确且签名算法严格匹配官方规范。
- 适用场景:对消息来源真实性有验证需求的内部系统或对外暴露的 webhook 接口。
- 前置准备:在钉钉机器人设置中获取 secret,并确保服务端支持 HMAC-SHA256 算法。
- 验收标准:发送测试消息后,钉钉后台显示发送成功且接收端无报错。
签名算法核心逻辑
钉钉机器人加签模式的核心目的是防止请求被篡改或重放。服务端通过时间戳和密钥生成签名,钉钉收到请求后会用同样的算法验证。如果时间戳偏差过大,或者签名计算过程中字符编码、换行符处理不一致,都会导致验证失败。
计算流程如下:
- 获取密钥:进入钉钉群机器人设置,安全设置中选择“加签”,复制显示的 secret 字符串,注意不要包含多余空格。
- 生成时间戳:获取当前时间戳,单位必须是毫秒(milliseconds),例如 Python 中用 time.time() * 1000 并取整。
- 计算签名:将时间戳字符串、换行符 \n、secret 字符串拼接,使用 HMAC-SHA256 算法计算摘要,再进行 Base64 编码,最后做 URL Encode。
- 拼接请求:将 access_token、timestamp、sign 三个参数拼接到 webhook URL 中,注意参数之间用 & 连接。
代码实现示例
签名生成建议使用脚本语言处理,当然也可以通过 shell 命令组合实现。以下是 Python 和 Shell 两种实现方式,可直接嵌入现有发送代码中。
Python 实现
import hmac
import hashlib
import base64
import urllib.parse
import time
def generate_sign(secret, timestamp):
string_to_sign = f'{timestamp}\n{secret}'
sign = hmac.new(secret.encode('utf-8'), string_to_sign.encode('utf-8'), digestmod=hashlib.sha256).digest()
sign = urllib.parse.quote_plus(base64.b64encode(sign).decode('utf-8'))
return sign
timestamp = str(round(time.time() * 1000))
secret = 'SECxxxxxxxx' # 替换为实际 secret
sign = generate_sign(secret, timestamp)
webhook = f'https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN×tamp={timestamp}&sign={sign}'Shell 实现
基于 openssl 和 curl 的命令行示例,注意 URL 编码部分需借助 perl 或 python 处理:
timestamp=$(date +%s%3N)
secret="SECxxxxxxxx"
string_to_sign="${timestamp}\n${secret}"
# 计算 HMAC-SHA256 并 Base64 编码
sign=$(printf '%s' "${string_to_sign}" | openssl dgst -sha256 -hmac "${secret}" -binary | base64)
# URL 编码 (使用 perl)
sign_encoded=$(perl -pe 's/([^\w_.~-])/sprintf("%%%02X", ord($1))/eg' <<< "${sign}")
webhook="https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN×tamp=${timestamp}&sign=${sign_encoded}"
curl "${webhook}" -H "Content-Type: application/json" -d '{"msgtype":"text","text":{"content":"测试消息"}}'验证与常见故障排查
发送请求后,检查钉钉接口返回的 JSON 数据。如果 errcode 为 0 且 errmsg 为 ok,说明签名验证通过。同时观察钉钉群内是否收到消息。
常见错误与排查
- 时间戳单位错误:必须使用毫秒级时间戳,秒级时间戳会导致验证失败。
- 换行符处理:拼接字符串时,时间戳和 secret 之间必须是标准的换行符 \n,不能是 \r\n 或空格。
- 编码问题:所有字符串处理必须统一使用 UTF-8 编码,尤其是在 Java 或 Go 等强类型语言中。
- URL 编码:签名生成后必须进行 URL Encode,否则特殊字符会导致请求参数解析错误。
- Secret 泄露:secret 等同于密码,不要提交到代码仓库,建议通过环境变量或配置中心管理。
如果返回 invalid sign 或 timestamp invalid,请优先检查上述时间戳和签名计算步骤。
参考来源
- 钉钉开放平台,自定义机器人接入文档,https://open.dingtalk.com/document/robots/custom-robot-access