上次聊了怎么用"搭积木"的方式搭后台,这次聊聊怎么把积木搭进企业生态——企微集成 + 能力开放平台。
上一篇《当 90% 的中后台还在重复造轮子》发出后,不少同学在评论区问了一个问题:
"框架内部能力确实全,但企业里不可能只用你一个系统。审批要推到企微,外部系统要调你的接口,这些怎么搞?"
好问题。说实话,这也是我做完低代码、工作流、AI 之后,一直在想的事。
一个后台框架如果只管自己内部那摊事,做得再好也是信息孤岛。
企业真实的长这样:
这些事单独拎出来都不难,难的是每接一个项目都要重来一遍。于是我把企业微信协同和能力开放做进了 Forge,今天聊聊这两块。
先放个演示地址,文章里说的所有东西都能在线试:http://www.dlforgelab.com:8084/forge/login (账号
admin/123456)
先说最大的误解——很多人以为"企微集成"就是加个企业微信扫码登录。不是。
| 能力 | 多数框架的现状 |
|---|---|
| 登录 | 支持企微扫码,但 OAuth 回调直接把用户信息丢给前端,前端再提交后端 |
| 通讯录 | 手动导入或写一次性脚本同步,人员变动靠人工记得改 |
| 消息 | 自己写企微 API 调用代码,散落在各个业务模块里 |
| 待办 | 企微收不到审批通知,或者收到后点了跳过去是一个没有鉴权的 URL |
| 安全 | Secret 明文存数据库,回调验签全靠自己手写,出了问题不好查 |
核心问题:每加一个平台(飞书、钉钉),这些逻辑全要再写一遍。
我没有把企微逻辑写死在登录、用户、消息、流程这些模块里。而是先做了一层平台无关的企业协同 SPI,企业微信只是它的第一个完整适配器。
架构长这样:
forge-starter-collaboration(通用 SPI 层)
├── Provider Connector 接口 ← 飞书/钉钉以后只需实现这个
├── 能力枚举(LOGIN/DIRECTORY/MESSAGE/TODO)
├── 统一上下文 + Provider Registry
└── 合同测试套件 + Fake Provider ← 证明编排层不依赖企微分支
forge-plugin-collaboration(编排插件)
├── 连接管理(一个租户可配多个企业连接)
├── 应用管理(每个连接下可挂多个物理应用,Secret 只存一份)
├── 通讯录同步(全量 + 增量 + 冲突处理 + 问题单)
├── 消息投递(统一 COLLABORATION 渠道,逐接收人结果)
├── 待办投影(Flowable 任务 → 企微卡片,状态始终以 Forge 为准)
└── 回调收件箱(验签/解密/防重放/去重/异步补偿)
一句话总结:飞书、钉钉后续只需实现 Connector,不动组织同步、消息、待办和运维主链路。
企业集成涉及身份认证、人员停用和流程操作,安全出问题就是大事。几个关键设计:
登录链路彻底重写了 OAuth 流程:
| 环节 | 旧做法(多数框架) | Forge 的做法 |
|---|---|---|
| OAuth state | 前端生成或不校验 | 服务端一次性生成并保存,绑定连接/租户/动作 |
| 回调返回 | 完整 AuthUser 返回前端 | 只返回短期一次性 socialTicket,前端拿不到第三方用户信息 |
| 身份来源 | 信任前端提交的 socialUuid | 只认服务端校验过的连接和外部身份 |
| 账号合并 | 手机号/邮箱相同自动合并 | 禁止自动合并,只认通讯录已建立的映射,防止错误合并员工 |
凭据安全:
所有 Secret、Token、Callback Token、EncodingAESKey 全部走版本化 AES-GCM 认证加密存储,管理端只返回"已配置"状态和固定掩码。空值保留(不改不动),轮换用比较更新防并发恢复旧值。认证加密不可用时失败关闭,不回退明文。
待办回调不能越权审批:
企微卡片点击后,Forge 重新校验:外部身份 → 接收人 → 租户 → 任务当前状态 → 办理权限。任务已完成、已撤回或操作者无权时,返回业务已失效并更新外部卡片,不重复执行。
| 能力 | 说明 |
|---|---|
| 全量同步 | 读取完整快照 → 校验父子关系 → 分阶段落库 → 成功后才处理未出现记录 |
| 增量处理 | 成员新增/变更/离职/转部门,定时全量校准兜底 |
| 权限边界 | 同步只管本连接拥有的映射,Forge 角色/组织内角色/手工岗位不覆盖 |
| 失败保护 | 拉取中断时不误停用存量人员(这个坑很常见) |
| 问题单 | 冲突、失败进队列,管理员可绑定/忽略/重试 |
| 同步日志 | 批次、阶段、计数、游标、状态全可查 |
"企微是通讯录权威来源,Forge 是权限权威来源"——这个边界很重要。同步不会误删你手工配的角色和权限。
新增了一整套管理界面(演示站可体验):
| 页面 | 干什么 |
|---|---|
| 连接管理 | 配置企业连接,管理登录/通讯录/消息/待办应用,Secret 永不明文返回 |
| 连通测试 | 按能力执行 Token/读取/测试消息验证,配完立刻知道行不行 |
| 同步日志 | 查看同步批次和阶段统计 |
| 问题单 | 冲突/失败队列,可人工处理 |
| 映射查看 | 查看外部部门/成员/岗位/标签与 Forge 的映射 |
| 投递管理 | 消息和待办投递状态,失败可人工重试 |
| 回调事件 | 回调元数据和 processing 状态(不返回解密正文) |
第二块是能力开放平台。这个更硬核——它解决的是"外部系统怎么安全地调你的接口"。
客户场景很真实:外围系统(ERP、小程序、第三方应用)需要调 Forge 的业务接口——建单、发起审批、查询数据。老做法是给每个对接方开一个账号,写一段鉴权代码,然后在 Controller 里手动校验权限。
问题在哪?
核心是一个统一 REST 开放网关:
外部请求
│
├── 1. 认证(OAuth Bearer 或 HMAC-SHA256 签名)
├── 2. 防重放(timestamp ±5分钟 + nonce 一次性校验)
├── 3. 授权(能力是否已发布?客户端是否已授权?)
├── 4. 身份校验(required_actor_type:USER/SERVICE/BOTH)
├── 5. 限流(读 120次/分,写 20次/分,可配置)
├── 6. 幂等(写操作强制 Idempotency-Key,命中返回首次响应快照)
├── 7. Schema 校验(按已发布版本校验请求体)
├── 8. 执行(CapabilityExecutor + ExecutionIdentity 上下文)
├── 9. 统一响应 {code, message, requestId, timestamp, data}
└── 10. 审计入库(不保存请求/响应原文)
这条链路上每一步都是可配置、可审计、可拒绝的。 任何一步不通过,直接返回明确的错误码,不往下走。
| 模式 | 认证方式 | 身份类型 | 适用场景 |
|---|---|---|---|
| OAuth Bearer | Authorization: Bearer fdu_... |
SERVICE 或 USER | 现代对接,支持用户委托 |
| HMAC 签名 | X-Forge-App-Id + timestamp + nonce + signature |
仅 SERVICE | 传统系统对接,不依赖 OAuth |
签名串固定为 appId\ntimestamp\nnonce\nMETHOD\npath\nsha256(body),HMAC-SHA256,常量时间比较。简单、安全、可复制。
关键安全设计: 签名密钥是 KEK 加密可逆存储(因为验签要还原原文),和现有 Bearer 凭据的哈希存储完全分离——不改动现有凭据的安全语义。
不是什么接口都能开放。能力要先注册、发布、授权,才能被外部调用:
| 能力来源 | 说明 | 安全控制 |
|---|---|---|
| 业务动作(BUSINESS_ACTION) | 低代码业务对象的增删改查 | 按字段白名单过滤 |
| 流程动作(FLOW_ACTION) | 发起流程、提交业务申请、审批 | 强制 USER 委托身份 |
| 系统服务(SYSTEM_SERVICE) | 代码显式注册的服务(如"启动已发布流程") | 管理端不可填任意 URL,只选注册项 |
系统服务是关键设计—— 管理员不能在后台填一个任意 URL 就开放出去。系统服务只能由代码注册,发布时固定流程模型和允许变量,调用者不能指定 modelKey、tenantId、userId 或 initiator。这避免了"把内部接口直接代理出去"的风险。
这是我花精力最多的地方。以前配完授权,管理员不知道行不行,要开发翻日志。现在:
四步式调用指南:
/oauth2/token + /openapi/v1/capabilities/:code/invoke,有副作用的能力二次确认认证方式一键切换: OAuth 和 HMAC 示例通过单一选择器切换,URL、能力编码、Header、Body 与真实网关契约完全一致。
文档下载:
一句话:管理员配完,自己就能判断能不能调、怎么调、为什么不能调。
外部系统调用流程审批类能力时,需要一个真实的 Forge 用户身份。Forge 提供两条路:
| 方式 | 说明 | 适用场景 |
|---|---|---|
| 受信 OIDC Token Exchange | 外围系统提交受信 IdP 签发的 JWT,Forge 按 issuer + subject 自动映射现有用户并签发短期 USER Token |
有统一身份提供方 |
| 客户端签名用户断言 | 无统一 OIDC 时,客户端用 RSA 私钥签发短期 JWT(最长 2 分钟),Forge 验签后按预绑定 sub 委托真实用户 |
无统一 IdP |
安全边界很明确:
none 和共享密钥subject_token_type,验签失败不互相回退每次调用(含被拒绝的)写 ai_capability_invocation_log,记录:
requestId / capabilityCode / version / clientId / actorType / actorUserId / tenantId / resultCode / httpStatus / schemaPath / durationMs
不保存: 请求 Body、响应 Body、Token、Secret、签名、Nonce 原文、用户手机号。
控制台可按 requestId 串联:入口接收 → 认证完成 → 授权完成 → 幂等命中 → 执行成功/失败。排障不用翻服务器日志。
企微集成和能力开放平台不是孤立的两个功能,拼在一起就是一条完整的企业集成链路:
┌─────────────────────────────────┐
│ Forge Admin │
│ │
企业微信 ───────→ │ 企微协同层 │
(通讯录/登录) │ ├── 人员同步 → sys_user │
│ ├── 安全登录 → Sa-Token 会话 │
│ ├── 消息投递 → 消息中心 │
│ └── 待办投影 → Flowable 任务 │
│ │
外部系统 ───────→ │ 能力开放平台 │
(ERP/小程序/ │ ├── OAuth/HMAC 认证 │
第三方应用) │ ├── 能力注册/发布/授权 │
│ ├── 网关调用 → CapabilityExecutor│
│ └── 审计追溯 → 调用日志 │
│ │
│ 低代码 + 工作流 + AI + 多租户 │
└─────────────────────────────────┘
一个真实场景走一遍:
以前这套东西从零搭,至少一个月。现在配一配,跑通验证,几天就能上线。
| 能力 | 自己从零搭 | 用 Forge |
|---|---|---|
| 企微通讯录同步 | 写同步脚本 + 增量处理 + 冲突处理 ≈ 5 人日 | 配置连接 + 连通测试 ≈ 半天 |
| 企微安全登录 | OAuth + state 校验 + 票据交换 ≈ 3 人日 | 配置连接 + 能力绑定 ≈ 2 小时 |
| 企微消息投递 | 企微 API + 逐人结果 + 重试 ≈ 3 人日 | 统一消息渠道,配置即用 |
| 企微待办卡片 | 任务投影 + 回调 + 鉴权 ≈ 5 人日 | 绑定流程 + 安全跳转 |
| 开放 API 网关 | 认证 + 防重放 + 限流 + 幂等 ≈ 8 人日 | 开启网关 + 注册能力 |
| 客户端授权管理 | 凭据 + 授权 + 审计 ≈ 5 人日 | 控制台配置,开箱即用 |
| 调用文档 | 手写文档 + 维护 ≈ 持续成本 | 自动生成 Markdown + OpenAPI |
| 飞书/钉钉扩展 | 全部重来 ≈ 15+ 人日 | 实现 Connector,不改主链路 |
粗算:自己搭这套企业集成,40-50 人日起步。用 Forge,配置 + 验证,一周内能跑通。
不吹,客观说几个当前的限制:
FORGE_CAPABILITY_OPEN_GATEWAY_ENABLED=false,需要显式开启。这是安全设计——不用的能力默认不暴露。所有文章里说的能力,演示站都可以在线试:
admin / 123456)
源码地址:
上篇"搭积木"聊的是怎么把内部能力做扎实。这篇聊的是怎么把这些能力接通企业生态——向内接企业微信,向外开能力网关。
企业后台说到底解决的是三件事:内部管得好、对内能协同、对外能开放。 缺一不可。
做企微集成时最深的感受是:安全边界比功能更重要。 登录不能信任前端身份、通讯录同步不能误删人员、待办回调不能越权审批——这些不是"做了就行",是"做对了才敢上"。所以你在文章里看到大量"失败关闭""禁止自动合并""重新校验"的设计,都是被真实场景教出来的。
做开放平台时最深的感受是:管理员体验决定成败。 网关安全做得再好,管理员配完不知道行不行、开发要翻日志排障,那这个平台就是半成品。所以调用指南、就绪诊断、在线测试、自动文档——这些"体验"功能花的精力不比安全链路少。
开源不易,持续迭代更难。如果觉得有用,点个 Star 是最大的鼓励。
你现在的企业集成方案是什么?企微对接最头疼的是哪块?评论区聊聊,想看哪块源码拆解的也可以扣——
我挑呼声最高的那块下一篇详细写。