n8n 配置飞书 Webhook 签名及 Crypto 节点用法
介绍如何在 n8n 中配置飞书自定义机器人 Webhook,重点讲解安全设置中的签名校验原理及实现。内容包括添加机器人步骤、Webhook 地址获取、关键词/IP/签名三种安全方式,以及利用 n8n Crypto 节点配合 HMAC-SHA256 算法计算签名的具体配置方法。同时提供了文本、富文本、卡片等多种消息类型的发送格式示例及常见问题解答。

介绍如何在 n8n 中配置飞书自定义机器人 Webhook,重点讲解安全设置中的签名校验原理及实现。内容包括添加机器人步骤、Webhook 地址获取、关键词/IP/签名三种安全方式,以及利用 n8n Crypto 节点配合 HMAC-SHA256 算法计算签名的具体配置方法。同时提供了文本、富文本、卡片等多种消息类型的发送格式示例及常见问题解答。

自定义机器人是一种只能在当前群聊中使用的机器人。该类机器人无需经过租户管理员审核,即可在当前群聊中通过调用 webhook 地址的方式完成消息推送。
企业存在给特定群组自动推送消息的场景,例如,推送监控报警、销售线索、运营内容等。在该类场景下,你可以在群组中添加自定义机器人,自定义机器人默认提供 webhook,通过服务端调用 webhook 地址,即可将外部系统的消息通知即时推送到群组中。自定义机器人也包含了 自定义关键词、IP 白名单 和 签名 三种维度的安全配置,便于控制 webhook 的调用范围。
机器人对应的 webhook 地址 格式如下:
https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxxxxxxxxxxxxx
请妥善保存好此 webhook 地址,不要公布在 Gitlab、博客等可公开查阅的网站上,避免地址泄露后被恶意调用发送垃圾消息。
后续你可以在群组名称右侧点击机器人图片,进入自定义机器人详情页,管理自定义机器人的配置信息。
用任意方式向 webhook 地址发起一个 HTTP POST 请求。你需要具备一定的服务端开发基础,通过服务端 HTTP POST 请求方式调用 webhook 地址。以 curl 指令为例,请求示例如下。
macOS
curl -X POST -H "Content-Type: application/json" -d '{"msg_type":"text","content":{"text":"request example"}}' https://open.feishu.cn/open-apis/bot/v2/hook/****
Windows (cmd)
curl -X POST -H "Content-Type: application/json" -d "{\"msg_type\":\"text\",\"content\":{\"text\":\"request example\"}}" https://open.feishu.cn/open-apis/bot/v2/hook/****
Windows (PowerShell)
curl.exe -X POST -H "Content-Type: application/json" -d '{\"msg_type\":\"text\",\"content\":{\"text\":\"requestexample\"}}' https://open.feishu.cn/open-apis/bot/v2/hook/****
示例命令说明:
{"msg_type":"text","content":{"text":"request example"}}https://open.feishu.cn/open-apis/bot/v2/hook/**** 为示例值,你在实际调用时需要替换为自定义机器人真实的 webhook 地址。执行命令后,进入自定义机器人所在群组查看测试消息。
如果请求成功,命令行将会回显以下信息:
{"StatusCode":0,"StatusMessage":"success","code":0,"data":{},"msg":"success"}
如果请求体格式错误,则会返回以下信息:
{"code":9499,"msg":"Bad Request","data":{}}
成功添加自定义机器人后,推荐你为自定义机器人添加安全设置,以保证机器人接收请求的安全性。
目前提供的安全设置方式如下:
我们强烈建议为自定义机器人添加安全设置,以提高安全性。在同一个自定义机器人中,你可以设置一个或多个方法。
注意:自定义关键词只对 text、title 这类文本参数值生效。
设置签名校验后,向 webhook 发送请求需要签名校验来保障来源可信。所校验的签名需要通过时间戳与秘钥进行算法加密,即将 timestamp + "\n" + 密钥 当做签名字符串,使用 HmacSHA256 算法计算空字符串的签名结果,再进行 Base64 编码。
本文提供了以下不同语言的代码示例,用于计算获得签名字符串。
package sign;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.codec.binary.Base64;
public class SignDemo {
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
String secret = "demo";
int timestamp = 1599360473;
System.out.printf("sign: %s", GenSign(secret, timestamp));
}
private static String GenSign(String secret, int timestamp) throws NoSuchAlgorithmException, InvalidKeyException {
// 把 timestamp+"\n"+密钥当做签名字符串
String stringToSign = timestamp + "\n" + secret;
// 使用 HmacSHA256 算法计算签名
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(stringToSign.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
byte[] signData = mac.doFinal(new byte[]{});
(Base64.encodeBase64(signData));
}
}
func GenSign(secret string, timestamp int64)(string,error){
// timestamp + key 做 sha256, 再进行 base64 encode
stringToSign := fmt.Sprintf("%v", timestamp)+"\n"+ secret
var data []byte
h := hmac.New(sha256.New,[]byte(stringToSign))
_, err := h.Write(data)
if err !=nil{return"", err }
signature := base64.StdEncoding.EncodeToString(h.Sum(nil))
return signature,nil
}
import hashlib
import base64
import hmac
def gen_sign(timestamp, secret):
# 拼接 timestamp 和 secret
string_to_sign = '{}\n{}'.format(timestamp, secret)
hmac_code = hmac.new(string_to_sign.encode("utf-8"), digestmod=hashlib.sha256).digest()
# 对结果进行 base64 处理
sign = base64.b64encode(hmac_code).decode('utf-8')
return sign
获取签名字符串后,在向 webhook 发送请求时,需要加上时间戳(timestamp)和签名字符串(sign)字段信息。
{
"timestamp": "1599360473",
"sign": "xxxxxxxxxxxxxxxxxxxxx",
"msg_type": "text",
"content": {"text": "request example"}
}
HMAC 的正确用法是:
hmac.new(key=secret, msg=data, digestmod=hashlib.sha256)
但是飞书提供的代码是:
string_to_sign = '{}\n{}'.format(timestamp, secret)
hmac_code = hmac.new(string_to_sign.encode("utf-8"), digestmod=hashlib.sha256).digest()
第一个参数 key 变成了 timestamp 和 secret 的拼接(用换行符),第二个参数 msg 为空。
所以我们节点配置如下:


{"timestamp": "{{Math.round(new Date().getTime()/1000)}}",}



{{ $json.timestamp + '\n' + 'XRkoNa93xgcXy5ZwB77PLh'}}


{
"nodes": [
{
"parameters": {
"method": "POST",
"url": "https://open.feishu.cn/open-apis/bot/v2/hook/************************",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"timestamp\": \"{{ $json.timestamp}}\",\n \"sign\": \"{{ $json.sign}}\",\n \"msg_type\": \"text\",\n \"content\": {\n \"text\": \"request example\"\n }\n}",
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [496, 0],
"id": "0cf5fa50-e7ef-405d-b1c9-5edc1dc49889"
在飞书群组的 设置 中,打开 群机器人 列表,找到需要删除的自定义机器人,在卡片右侧点击删除图标。
向自定义机器人 webhook 地址发送 POST 请求时,支持推送的消息格式有 文本、富文本、图片消息 以及 群名片 等。
请求消息体示例
{"msg_type":"text","content":{"text":"新更新提醒"}}
参数说明
| 字段 | 类型 | 是否必填 | 示例值 | 描述 |
|---|---|---|---|---|
| text | string | 是 | Test content | 文本内容。 |
文本消息的 @ 用法
// @ 单个用户 <at user_id="ou_xxx">名字</at>
// @ 所有人 <at user_id="all">所有人</at>
文本消息 @ 用法示例
{"msg_type":"text","content":{"text":"<at user_id=\"ou_xxx\">Tom</at> 新更新提醒"}}
请求消息体示例
{"msg_type":"post","content":{"post":{"zh_cn":{"title":"项目更新通知","content":[[{"tag":"text","text":"项目有更新:"},{"tag":"a","text":"请查看","href":"http://www.example.com/"},{"tag":"at","user_id":"ou_18eac8********17ad4f02e8bbbb"}]]}}}}
参数说明
请求消息体示例
{"msg_type":"share_chat","content":{"share_chat_id":"oc_f5b1a7eb27ae2****339ff"}}
请求消息体示例
{"msg_type":"image","content":{"image_key":"img_ecffc3b9-8f14-400f-a014-05eca1a4310g"}}
飞书卡片是一种轻量的消息推送应用。发送卡片时,需要将消息体的 content 字符串替换为 card 结构体,并对整个请求消息体进行 JSON 转义。
请求消息体示例
{"msg_type":"interactive","card":{"schema":"2.0","config":{"update_multi":true},"body":{"direction":"vertical","elements":[{"tag":"markdown","content":"西湖,位于中国浙江省杭州市西湖区龙井路 1 号..."}]},"header":{"title":{"tag":"plain_text","content":"今日旅游推荐"}}}}
你可以在机器人发送的普通文本消息(text)、富文本消息(post)、消息卡片(interactive)中,使用 at 标签实现 @ 人效果。
自定义机器人本身不能调用接口获取用户的 open_id。你可以开发一个机器人应用,使用受管控的方案获得用户的 open_id。
方案一:通过邮箱或手机号反查用户的 open_id
方案二:解析用户发送给机器人的带 @ 人内容的消息,获取目标用户的 open_id
不能,自定义机器人只能用于在群聊中自动发送通知,不能响应用户 @ 机器人的消息。
自定义机器人自身无法撤回自己发送的消息,必须由群聊内的群主或管理员进行撤回。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online