From 8ff1c57b83612f5ab85d63c0097574310f2af9c3 Mon Sep 17 00:00:00 2001 From: baiyanyun Date: Sun, 7 Jun 2026 11:33:59 +0800 Subject: [PATCH] fix(backend): validate digital sync client key header --- .../DigitalEmployeeSyncController.java | 23 ++++++++++++++++++- ...ital-employee-frontend-integration-plan.md | 1 + 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-ui/src/main/java/com/xspaceagi/agent/web/ui/controller/DigitalEmployeeSyncController.java b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-ui/src/main/java/com/xspaceagi/agent/web/ui/controller/DigitalEmployeeSyncController.java index 67bdd5e4..88e9fd54 100644 --- a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-ui/src/main/java/com/xspaceagi/agent/web/ui/controller/DigitalEmployeeSyncController.java +++ b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-ui/src/main/java/com/xspaceagi/agent/web/ui/controller/DigitalEmployeeSyncController.java @@ -7,7 +7,9 @@ import com.xspaceagi.agent.core.adapter.dto.digital.DigitalEmployeeSyncResponseD import com.xspaceagi.system.spec.annotation.RequireResource; import com.xspaceagi.system.spec.dto.ReqResult; import jakarta.annotation.Resource; +import org.apache.commons.lang3.StringUtils; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; @@ -19,11 +21,30 @@ import static com.xspaceagi.system.spec.enums.ResourceEnum.CONTENT_DIGITAL_EMPLO @RequestMapping("/api/digital-employee/sync") public class DigitalEmployeeSyncController { + private static final String CLIENT_KEY_HEADER = "X-Qiming-Client-Key"; + @Resource private DigitalEmployeeSyncApplicationService digitalEmployeeSyncApplicationService; @RequestMapping(path = "/outbox", method = RequestMethod.POST) - public ReqResult syncOutbox(@RequestBody DigitalEmployeeSyncRequestDto request) { + public ReqResult syncOutbox( + @RequestHeader(name = CLIENT_KEY_HEADER, required = false) String headerClientKey, + @RequestBody(required = false) DigitalEmployeeSyncRequestDto request + ) { + if (StringUtils.isBlank(headerClientKey)) { + return ReqResult.create("0001", "客户端 Key header 缺失", null); + } + if (request == null) { + request = new DigitalEmployeeSyncRequestDto(); + } + String normalizedHeaderClientKey = headerClientKey.trim(); + if (StringUtils.isBlank(request.getClientKey())) { + request.setClientKey(normalizedHeaderClientKey); + } else if (!StringUtils.equals(request.getClientKey().trim(), normalizedHeaderClientKey)) { + return ReqResult.create("0001", "客户端 Key header 与请求体不一致", null); + } else { + request.setClientKey(request.getClientKey().trim()); + } return ReqResult.success(digitalEmployeeSyncApplicationService.syncOutbox(request)); } diff --git a/qimingclaw/docs/digital-employee-frontend-integration-plan.md b/qimingclaw/docs/digital-employee-frontend-integration-plan.md index dee3839e..bf589e25 100644 --- a/qimingclaw/docs/digital-employee-frontend-integration-plan.md +++ b/qimingclaw/docs/digital-employee-frontend-integration-plan.md @@ -466,6 +466,7 @@ window.QimingClawBridge.digital.flushSync() - 管理端返回格式包含 `acked[]`,每项带 `outbox_id`、`entity_type`、`entity_id`、`remote_id`。 - 若管理端只 ack 批次中的部分 item,已 ack 的 item 会标记 synced,未 ack 的 item 会保留 failed 状态并进入下一轮补偿。 - 当前接口沿用管理端登录态鉴权,客户端必须能读取到客户端 Key 和登录 ticket/token 才会推送;缺少凭据时保持本地 outbox 等待,并在数字员工同步状态条显示缺少项。 +- 管理端接收 outbox 时会校验 `X-Qiming-Client-Key`,请求体缺少 `client_key` 时以 header 为准,二者不一致时拒绝写入。 后端最小落点: