飞书自定义机器人 Webhook 接入实战
飞书机器人支持通过 Webhook 接收消息,常用于系统告警通知。集成时需注意安全设置与签名校验,以下是 Java 与 Python 的完整实现方案。
配置自定义机器人
首先将机器人邀请至目标群组,并在群设置中启用自定义机器人。获取 Webhook 地址后,务必妥善保管,切勿公开在 Git 仓库或博客中,防止被恶意调用。
[图:飞书群机器人设置界面]
进入安全设置区域,开启签名校验。系统默认提供密钥,也可手动重置。这里需要记录两个关键信息:Webhook URL 和 Secret 密钥。
[图:签名校验设置界面]
核心逻辑说明
发送消息前需完成签名校验,这是飞书的安全机制。
- 时间戳:必须为秒级,且注意时区处理。直接使用
System.currentTimeMillis()除以 1000 可能因毫秒精度导致验证失败,建议转换为 UTC 秒值。 - 签名算法:使用 HmacSHA256,签名字符串格式为
timestamp\nsecret。 - 消息体:支持富文本(post),需按官方 JSON 结构组装 content。
Java 实现示例
Java 端主要涉及签名计算与 HTTP 请求封装。下面代码展示了如何生成签名并构建富文本消息体。
public class FeishuWebhook {
private static final Logger logger = LoggerFactory.getLogger(FeishuWebhook.class);
public static final String DEFAULT_DATETIME_FORMATTER_STR = "yyyy-MM-dd HH:mm:ss";
public static final DateTimeFormatter DEFAULT_DATETIME_FORMATTER = DateTimeFormatter.ofPattern(DEFAULT_DATETIME_FORMATTER_STR);
public static void send(String url, String secret, AlertDO alertDO) {
logger.info("FeishuWebhook.send, url:{}, alertDO={}", url, alertDO);
JSONObject createRequestBody(secret, alertDO);
logger.info(, requestBody);
HttpUtils.postForJsonObject(url, , , requestBody);
logger.info(, result);
}
String
NoSuchAlgorithmException, InvalidKeyException {
timestamp + + secret;
Mac.getInstance();
mac.init( (stringToSign.getBytes(StandardCharsets.UTF_8), ));
[] signData = mac.doFinal( []{});
(Base64.encodeBase64(signData));
}
JSONObject {
();
LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().getEpochSecond();
logger.info(, seconds);
{
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));
} (Exception e) {
e.printStackTrace();
}
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(createInnerHeadContent(tag));
item.add(createInnerTextContent(text));
item;
}
JSONObject {
();
result.put(, );
result.put(, tag + );
result;
}
JSONObject {
();
result.put(, );
result.put(, text);
result;
}
}


