Skip to content

XHIM 前置策略与事后业务 Webhook

XHIM 提供两条用途不同的业务集成链路:

类型时机可影响原操作可靠性语义版本
事务 Outbox 事后 Webhook数据提交成功后HMAC、重试、Dead Letter、管理后台补投Business/Enterprise
同步前置策略 Webhook数据提交前可允许、拒绝或安全改写有界超时,显式 fail-open/fail-closedEnterprise/OEM

不要用事后 Webhook 做内容拦截,因为响应返回时消息已经提交;也不要用前置策略 同步耗时业务数据,它会增加用户操作延迟。

1. 配置

dotenv
XHIM_PRODUCT_EDITION=enterprise
XHIM_POLICY_WEBHOOK_URL=https://business.example.com/xhim/policy
XHIM_POLICY_WEBHOOK_KEY_ID=policy-2026-01
XHIM_POLICY_WEBHOOK_SECRET_BASE64=<至少32字节随机密钥的Base64>
XHIM_POLICY_WEBHOOK_REQUEST_TIMEOUT=5s
XHIM_POLICY_WEBHOOK_FAILURE_MODE=deny
XHIM_POLICY_WEBHOOK_EVENTS=message.send.before,group.create.before

事件列表留空表示启用全部支持事件:

text
message.send.before
message.mutate.before
friend.request.before
group.create.before
group.members.change.before
group.join.request.before
group.governance.before

failure_mode=deny 在超时、网络失败、非 2xx、超大或非法响应时返回 policy_unavailable(HTTP 503);allow 会继续原操作,适合更重视可用性的业务。 业务明确拒绝返回 policy_rejected(HTTP 403)。

2. 请求签名

服务端发送 JSON POST,并附带:

text
X-XHIM-Policy-Timestamp: 1786074000
X-XHIM-Policy-Key-ID: policy-2026-01
X-XHIM-Policy-Version: 1.0
X-XHIM-Policy-Request-ID: policy-01...
X-XHIM-Policy-Signature: sha256=<hex>

签名原文:

text
HMAC-SHA256(secret, timestamp + "." + raw_request_body)

接收方必须先按 Key ID 找密钥,校验原始 Body 的常量时间 HMAC,再校验时间窗与 Request ID 幂等。不要先反序列化再重新编码后验签。

消息请求示例:

json
{
  "version": "1.0",
  "request_id": "policy-01...",
  "app_id": "com.customer.chat",
  "type": "message.send.before",
  "requested_at": "2026-08-07T09:00:00Z",
  "actor": {
    "user_id": "user-internal-id",
    "account_id": "account-id",
    "device_id": "ios-device",
    "platform": "ios"
  },
  "trace": {
    "operation_id": "send-20260807-1",
    "trace_id": "trace-1",
    "client_id": "client-message-1"
  },
  "proposal": {
    "conversation_id": "direct-...",
    "client_message_id": "client-message-1",
    "content_type": "text/plain",
    "content_version": 1,
    "payload_base64": "5b6e5aW977yM",
    "fallback_text": "原始摘要"
  }
}

3. 响应

允许:

json
{"decision":"allow"}

拒绝:

json
{
  "decision": "deny",
  "code": "content_blocked",
  "reason": "命中客户内容策略"
}

安全改写文字/消息 Payload:

json
{
  "decision": "allow",
  "patch": {
    "payload_base64": "5bey5pWP5YaF5a65XQ==",
    "fallback_text": "[敏感内容]"
  }
}

服务端只接受每类事件明确声明的 Patch 字段,出现未知字段会按无效策略响应处理。 改写后的命令会重新计算 Payload Hash、重新解析媒体和 @ 受众,并重新执行领域 校验与权限校验。策略服务不能修改 App、操作者、会话、幂等 ID、revision 或消息 类型来绕过服务端权限。

事件允许改写字段
message.send.beforepayload_base64fallback_text
message.mutate.before仅文字编辑可改 payload_base64fallback_text;撤回不可改内容
friend.request.beforeintroduction
group.create.beforetitlemember_user_idsinitial_administrator_user_ids
group.members.change.beforeadd_user_idsremove_user_ids
group.join.request.beforeintroduction
group.governance.before群资料、入群/成员可见性等对应的安全字段

4. 生产要求

  • URL 使用 HTTPS,禁止 URL 用户名密码、Query 和 Fragment;
  • 策略服务 P99 必须明显低于配置超时,不能同步调用慢速 OA 页面;
  • 对内容审核设置独立熔断、限流和可观测性;
  • fail-open 必须在客户安全评审中明确记录;
  • 拒绝原因可记录到业务审计,但不向普通客户端暴露敏感规则细节;
  • Key 轮换期间同时接受新旧 Key ID,完成重叠窗口后再退役旧密钥。

5. 批量迁移的事后事件边界

批量迁移不会把每一个导入对象伪装成一次日常用户操作:

  • 好友关系导入只产生一个 friendships.imported after-event;
  • 历史消息导入只产生一个 message.history.imported after-event,不产生 逐消息 message.sent after-event。

message.history.imported 的 resource 只包含导入作业 ID、会话 ID、最后服务端 序列和导入数量;trace 只包含 trace_idoperation_id。信封不含 消息 payload、fallback、sender 列表、source system 或 source message ID。 接收方应以 event ID 幂等消费这一个聚合事件,不应按导入数量期待多次回调。

6. Push 与 Presence 事后事件

Push 注册、客户端停用、管理员停用和 Provider 判定 Token 失效,均在设备状态 事务内追加耐久 after-event:push.device.registeredpush.device.disabled。信封只投影用户 ID、设备 ID、平台、启用状态和容量结果; Provider Token、环境/区域参数与 Provider 原始错误不会进入 Webhook。

presence.changed 只在用户的聚合 online|away|offline 状态或已排序活跃平台集合 发生变化时追加。普通续租心跳不产生业务事件。事件不包含 Account ID、Session ID、 TTL、ExpiresAt 或客户端心跳参数;接收方以 event ID 幂等,并按 resource.sequence 处理顺序或重放。

XHIM 客户端 SDK 与服务端文档