钉钉机器人发送消息返回 invalid sign 错误怎么解决

文章导读
出现 invalid sign 错误通常是因为开启了加签安全设置但请求参数计算不正确,最推荐的处理方向是核对机器人后台的 Secret 并在代码中严格按照 HMAC-SHA256 算法生成签名。
📋 目录
  1. 签名计算核心逻辑
  2. 多语言代码示例
  3. Curl 命令测试
  4. 验证与排查
A A

出现 invalid sign 错误通常是因为开启了加签安全设置但请求参数计算不正确,最推荐的处理方向是核对机器人后台的 Secret 并在代码中严格按照 HMAC-SHA256 算法生成签名。

先说结论:这是签名验证失败,需要重新计算 sign 参数并拼接到 webhook 地址中。

  • 先确认:机器人安全设置是否开启了“加签”模式。
  • 先处理:检查代码中 timestamp 单位和字符串拼接格式。
  • 再验证:使用 curl 或 Postman 手动发送请求测试。

签名计算核心逻辑

钉钉机器人加签机制要求客户端使用 HMAC-SHA256 算法生成签名。以下是标准的签名生成逻辑,请确保每一步都与官方规范一致:

timestamp = 当前时间毫秒数(13 位)
stringToSign = timestamp + "\n" + secret
sign = URL Encode( Base64( HMAC-SHA256( secret, stringToSign ) ) )
请求 URL = webhook + "×tamp=" + timestamp + "&sign=" + sign

注意这里的换行符是实际的换行字符(ASCII 0x0A),在大多数编程语言中可使用转义字符 "\n" 表示。待签名字符串中必须包含这个换行符,否则计算出的签名必然错误。

钉钉机器人发送消息返回 invalid sign 错误怎么解决

多语言代码示例

以下提供 Python 和 Java 的完整签名计算代码,可直接参考集成。

Python 示例

import time
import hmac
import hashlib
import base64
import urllib.parse

def get_dingtalk_sign(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))
    return timestamp, sign

# 使用示例
# timestamp, sign = get_dingtalk_sign("YOUR_SECRET")
# url = webhook + "×tamp=" + timestamp + "&sign=" + sign

Java 示例

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.util.Base64;

public class DingTalkSign {
    public static String[] getSign(String secret) throws Exception {
        Long timestamp = System.currentTimeMillis();
        String stringToSign = timestamp + "\n" + secret;
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));
        byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
        String sign = URLEncoder.encode(new String(Base64.getEncoder().encode(signData)), "UTF-8");
        return new String[]{String.valueOf(timestamp), sign};
    }
}

Curl 命令测试

在集成代码前,建议先使用 curl 命令手动验证签名是否生效。请将下方的占位符替换为实际值:

钉钉机器人发送消息返回 invalid sign 错误怎么解决
curl 'https://oapi.dingtalk.com/robot/send?access_token=YOUR_ACCESS_TOKEN×tamp=YOUR_TIMESTAMP&sign=YOUR_SIGN' \
-H 'Content-Type: application/json' \
-d '{"msgtype":"text","text":{"content":"测试消息"}}'

其中 YOUR_TIMESTAMP 和 YOUR_SIGN 需通过上述代码逻辑计算得出。如果返回 errcode 为 0,则签名验证通过。

验证与排查

发送请求后,观察返回的 JSON 数据。如果 errcode 为 0 且 errmsg 为 ok,说明签名通过。如果依旧报错,请按照以下步骤排查:

  • Secret 复制错误:确保前后没有带空格,不要复制换行符。建议在代码中打印 secret 长度进行核对。
  • 换行符误解:stringToSign 中的换行是真实换行符,检查代码中拼接时是否漏掉了 "\n"。
  • URL 编码:计算出的 sign 需要做 URL Encode,否则特殊字符会导致请求解析失败。
  • 时间戳过期:timestamp 与服务器时间差太大也可能导致失败,确保服务器时间同步,且 timestamp 必须是毫秒级(13 位)。
  • 错误码对照:若返回其他错误码,可查阅钉钉开放平台文档获取具体含义。

参考来源:钉钉开放平台 - 自定义机器人接入文档(open.dingtalk.com)