飞书自定义机器人 Webhook 接入指南
在开发自动化通知或报警系统时,将消息推送到飞书群聊是一个非常实用的需求。飞书提供了自定义机器人功能,通过 Webhook 地址即可发送消息。本文将介绍如何配置机器人,并给出 Java 和 Python 的完整集成示例。
配置自定义机器人
首先需要在目标群组中邀请自定义机器人。进入群设置,点击添加机器人,选择自定义机器人进行配置。这里需要重点记录两个信息:Webhook 地址和密钥(Secret)。
Webhook 地址格式通常为 https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxxxxxxxxxxxxx。请务必妥善保管此地址,避免公开泄露导致被恶意调用。
安全设置方面,建议开启签名校验。系统默认提供一个密钥,你也可以随时重置。开启后,发送请求时必须携带正确的签名,否则飞书服务端会拒绝接收。
核心逻辑:签名校验
为了保障通信安全,飞书要求所有 POST 请求必须包含时间戳和签名。签名的生成规则是:将时间戳、换行符和密钥拼接成字符串,使用 HmacSHA256 算法计算哈希值,最后进行 Base64 编码。
这里有一个容易踩坑的地方:时间戳必须是秒级整数,且要注意时区问题。直接使用 System.currentTimeMillis() / 1000 在某些场景下可能不够精确,建议使用 LocalDateTime 配合时区转换来获取当前秒数。
Java 实现方案
Java 端实现主要涉及三个部分:签名生成、JSON 消息体构造、HTTP 请求发送。下面是一个完整的工具类示例,封装了富文本消息的构建逻辑。
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
public class FeishuWebhook {
private static final String DEFAULT_DATETIME_FORMATTER_STR = "yyyy-MM-dd HH:mm:ss";
private static final DateTimeFormatter DEFAULT_DATETIME_FORMATTER = DateTimeFormatter.ofPattern(DEFAULT_DATETIME_FORMATTER_STR);
public static void send(String url, String secret, AlertDO alertDO) {
createRequestBody(secret, alertDO);
}
String Exception {
timestamp + + secret;
Mac.getInstance();
mac.init( (stringToSign.getBytes(StandardCharsets.UTF_8), ));
[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
(Base64.encodeBase64(signData));
}
JSONObject Exception {
();
LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().getEpochSecond();
genSign(secret, seconds);
requestBody.put(, seconds);
requestBody.put(, sign);
requestBody.put(, );
;
alertDO.getMessage();
alertDO.getDetail();
DEFAULT_DATETIME_FORMATTER.format(
LocalDateTime.ofInstant(alertDO.getStartTime().toInstant(), ZoneId.systemDefault()));
DEFAULT_DATETIME_FORMATTER.format(
LocalDateTime.ofInstant(alertDO.getEndTime().toInstant(), ZoneId.systemDefault()));
requestBody.put(, createOuterContent(title, message, detail, startTime, endTime));
requestBody;
}
JSONObject {
();
result.put(, createPostJsonObject(title, message, detail, startTime, endTime));
result;
}
JSONObject {
();
result.put(, createZhCNJsonObject(title, message, detail, startTime, endTime));
result;
}
JSONObject {
();
result.put(, title);
result.put(, createContentList(message, detail, startTime, endTime));
result;
}
JSONArray {
();
result.add(createItem(, message));
result.add(createItem(, detail));
result.add(createItem(, startTime));
result.add(createItem(, endTime));
result;
}
JSONArray {
();
item.add(createHead(tag));
item.add(createText(text));
item;
}
JSONObject {
();
result.put(, );
result.put(, tag + );
result;
}
JSONObject {
();
result.put(, );
result.put(, text);
result;
}
}


