前置依赖:add-request-auth-replay-guard(基座 UtilSignature / NonceCache 已落地)
mjava-com 的 CallerAuthInterceptor 不重新实现 HMAC 签名与 Nonce 去重,而是直接调用基座提供的 UtilSignature.sign() 与 NonceCache.putIfAbsent();本 change 只负责"按 callerId 动态查 secret"与"按 callerId 限流"两层。
External System → POST /api/com/{vendor}/{action}
Header: X-Caller-Id: {callerId}
X-Signature: {HMAC-SHA256(callerSecret, body+timestamp)}
X-Timestamp: {unix ms}
Body: { ...params aligned with base Client method args }
↓
CallerAuthInterceptor
├── 校验 X-Timestamp 5 分钟内
├── CallerRegistryService.get(callerId) → callerSecret
├── 计算 HMAC 对比
└── 通过 → 继续;失败 → 401
↓
GatewayController
├── 查 ActionWhitelist:{vendor}.{action} 是否开放
├── 用反射或注册的 handler 调对应基座 Client 方法
└── 包 McR 返回
字段约定:
| 宜搭字段 | 含义 |
|---|---|
textField_callerId |
调用方唯一 ID(签发时生成,如 caller-YDCBC-001) |
textField_callerSecret |
HMAC 密钥(生成时返回给调用方,服务端也存,需加密字段) |
textField_callerName |
可读名称(便于审计) |
textareaField_allowedActions |
JSON 数组:["dingtalk.user.get", "aliwork.form.save"],精确白名单 |
numberField_rateLimit |
每秒限流上限 |
radioField_enabled |
on / off |
dateField_expireAt |
密钥过期时间(可选) |
为避免反射开销和控制边界,每个开放的 action 在代码里显式注册:
@Component
public class DingtalkActionRegistry {
@PostConstruct
public void register(ActionRegistry reg) {
reg.register("dingtalk.user.get", (ctx, body) -> {
DDConf conf = resolveConf(ctx); // 可选:按 caller 配置不同 dingtalk 应用
return ddClient.getUserDetail(conf, body.getString("userid"));
});
}
}
application.yml 只维护"哪些 action 默认开放"的白名单;细粒度调用方权限通过宜搭权限表单的 allowedActions 限制。
RateLimiter 按 callerId 维度429 { code: "RATE_LIMITED" }继承 mjava-baseline §3.5 审计规范,追加字段:
| 字段 | 说明 |
|---|---|
callerId |
调用方 ID |
vendorAction |
{vendor}.{action} |
signatureValid |
签名校验结果 |
日志输出 ./log/{日期}/com-{callerId}.log(按调用方分片)。
callerSecret 存宜搭"加密字段"类型;内存访问后不得回传前端或记录到日志X-Timestamp 5 分钟外拒绝 + 可选 Nonce(Phase 1 不做,5 分钟窗口足够内部场景)WARN,连续 5 次同 callerId 失败触发 ERROR 告警(ExceptionNotice 已有通道)