From fc393cd685616b64dcb5120557a37f4ebc1b8561 Mon Sep 17 00:00:00 2001 From: richjohnrambo Date: Mon, 21 Sep 2026 14:00:25 +0800 Subject: [PATCH] docs: add aliyun realname integration notes --- docs/third/IM-阿里云实名认证接入方案.md | 536 ++++++++++++++++++ .../IM-阿里云实名认证租户配置字段SQL.sql | 13 + 2 files changed, 549 insertions(+) create mode 100644 docs/third/IM-阿里云实名认证接入方案.md create mode 100644 docs/third/IM-阿里云实名认证租户配置字段SQL.sql diff --git a/docs/third/IM-阿里云实名认证接入方案.md b/docs/third/IM-阿里云实名认证接入方案.md new file mode 100644 index 0000000..719503c --- /dev/null +++ b/docs/third/IM-阿里云实名认证接入方案.md @@ -0,0 +1,536 @@ +# IM 阿里云实名认证接入方案 + +## 1. 目标 + +本文记录当前 IM 实名认证能力的实现情况,以及后续接入阿里云实名认证的后端、前端和数据模型改造方案。接入目标是:用户在客户端提交实名信息后,由 IM Server 统一调用阿里云完成身份核验,客户端只感知认证状态和必要的认证跳转参数,不接触阿里云密钥、AccessKey、服务端回调密钥等敏感配置。 + +阿里云侧建议优先使用金融级实人认证/ID Verification(Cloudauth)能力。产品选型分两类: + +1. **姓名 + 身份证二要素核验**:体验最轻,适合只要求姓名和证件号一致性的场景。结果可同步或准同步返回。 +2. **增强版实人认证/活体核身**:服务端发起认证并获取 `CertifyId`,客户端通过阿里云 SDK、H5 或认证 URL 完成活体流程,服务端再查询结果或接收回调。适合强实名、风控和合规要求更高的场景。 + +当前产品原型只有姓名和证件号码输入页,但考虑后续安全要求,后端状态机需要兼容增强版实人认证。 + +## 2. 当前已有实现 + +### 2.1 前端对接接口 + +现有前后端文档已定义实名认证页面的三个核心入口: + +| 能力 | 接口 | 当前状态 | +| --- | --- | --- | +| 查询实名状态 | `GET /v1/im/security/realname` | 已实现 | +| 提交实名信息 | `POST /v1/im/security/realname/submissions` | 已实现 | +| 实名更正申诉 | `POST /v1/im/security/realname/appeals` | 已实现 | + +查询接口返回: + +```json +{ + "status": "unverified", + "can_submit": true +} +``` + +已提交或已通过时返回 `submission_id`、`masked_name`、`masked_id_number`、`verified_at`、`review_reason` 等字段。 + +提交接口要求: + +- 必须携带 `Authorization: Bearer `。 +- 必须携带 `Idempotency-Key`,服务端根据用户、租户和幂等键生成 `submission_id`。 +- Body 目前为: + +```json +{ + "name": "张三", + "id_type": "cn_id", + "id_number": "110101199001011234" +} +``` + +校验规则: + +- `name` 长度 2-32 个字符。 +- `id_type` 当前只支持 `cn_id`。 +- `id_number` 当前只支持 18 位中国大陆身份证号,末位允许 `X`。 + +### 2.2 服务端实现 + +当前代码主要在以下位置: + +| 模块 | 文件 | 说明 | +| --- | --- | --- | +| HTTP 路由 | `server/main.go` | 注册 `/v1/im/security/realname`、`/realname/submissions`、`/realname/appeals` | +| HTTP Handler | `server/im_http.go` | 查询、提交、申诉逻辑 | +| Store 接口 | `server/store/store.go` | `GetRealName`、`CreateRealNameSubmission`、`CreateRealNameAppeal` | +| MySQL 实现 | `server/db/mysql/tenant_adapter.go` | 租户库读写实名表 | +| 类型定义 | `server/store/types/security.go` | `RealNameVerification`、`RealNameAppeal` | +| 内部后台查询 | `server/internal_admin_query_resources.go` | `realname-verifications` 查询资源 | + +当前实名提交成功后只落库为 `pending`,不会调用第三方,也没有自动审核通过。重复提交时,如果最新状态是 `pending` 或 `verified`,服务端会直接返回当前状态,避免重复创建申请。 + +### 2.3 当前数据表 + +租户库已有 `im_realname_verifications`: + +| 字段 | 说明 | +| --- | --- | +| `id` | 自增主键 | +| `user_id` | 用户 ID | +| `submission_id` | 实名提交 ID,幂等唯一 | +| `status` | 当前状态,已有逻辑使用 `pending`、`verified`、`rejected`、`unverified` | +| `name_cipher` | 姓名保护存储值 | +| `id_type` | 证件类型 | +| `id_number_cipher` | 证件号保护存储值 | +| `id_number_hash` | 证件号哈希,用于重复身份检测 | +| `masked_name` | 脱敏姓名 | +| `masked_id_number` | 脱敏证件号 | +| `review_reason` | 审核原因或驳回原因 | +| `verified_at` | 通过时间 | +| `created_at` / `updated_at` | 创建和更新时间 | + +租户库已有 `im_realname_appeals`: + +| 字段 | 说明 | +| --- | --- | +| `id` | 雪花 ID | +| `appeal_id` | 申诉 ID | +| `user_id` | 用户 ID | +| `reason` | 申诉原因 | +| `attachments_json` | 附件列表 | +| `status` | 申诉状态,默认 `pending` | +| `review_comment` | 审核备注 | +| `created_at` / `updated_at` | 创建和更新时间 | + +### 2.4 已记录的增强项 + +`docs/IM-未实现后续增强项.md` 和 `docs/IM-需求实现情况审核报告.md` 已明确:实名状态查询、提交、申诉已经具备,但缺少后台审核流转、第三方认证结果回写、审核结果通知和完整申诉处理工单。 + +当前缺口如下: + +| 缺口 | 影响 | +| --- | --- | +| 未接第三方实名供应商 | 提交后长期停留 `pending`,无法自动通过或拒绝 | +| 缺少 provider 字段 | 无法区分人工审核、阿里云二要素、阿里云活体等来源 | +| 缺少第三方任务 ID | 无法通过 `CertifyId` 或外部 request id 查询认证结果 | +| 缺少回调/查询状态机 | 无法安全处理异步认证完成、重试和重复通知 | +| 缺少后台审核动作 | 人工通过、拒绝、申诉处理还没有完整闭环 | +| 缺少结果通知 | 实名通过或拒绝后,还没有统一系统通知事件 | + +## 3. 阿里云接入总体方案 + +### 3.0 当前联调结论 + +2026-09-20 已按 `docs/third/三方服务依赖信息.md` 中的阿里云 AccessKey 做过真实接口联调: + +| 账号 | 调用接口 | 结果 | 结论 | +| --- | --- | --- | --- | +| 子账号 `t1@1624085816137202.onaliyun.com` | `Id2MetaVerify` | `403 Forbidden.NoPermission`,缺少 `yundun-cloudauth:Id2MetaVerify` | 子账号需要 RAM 授权 | +| 主账号 AccessKey | `Id2MetaVerify` | 业务码 `410`,提示“未开通服务” | 当前阿里云账号尚未开通身份证二要素/ID_2META 服务 | + +因此代码已接入真实阿里云 API,但生产可用还需要: + +1. 在阿里云开通实人认证服务中的身份证二要素核验 `ID_2META`。 +2. 给运行时使用的 RAM 子账号授权 `yundun-cloudauth:Id2MetaVerify`,或授予对应实人认证服务策略。 +3. 在 `hotgo.hg_sys_config` 的 `im_realname` 分组写入 `realname_provider=aliyun_cloudauth`、`aliyun_access_key_id`、`aliyun_access_key_secret`、`aliyun_region_id`、`aliyun_endpoint`。 +4. 租户 `hotgo.im_tenant_config.realname_review_policy=1` 后,IM Server 才会自动调用阿里云;服务未开通、权限不足或返回不一致时都会进入 `manual_review`。 + +### 3.1 配置来源 + +实名认证配置分两层: + +1. **全局供应商配置**:阿里云 AccessKey、Endpoint、回调密钥等服务商级参数放在 HotGo 控制库 `hotgo.hg_sys_config`,使用独立分组 `im_realname`。 +2. **租户审核策略**:审核方式放在 HotGo 控制库的 `im_tenant_config`。该策略每个租户单独控制,默认 `0=不审核`。 + +全局供应商配置: + +| group | key | 建议值 | 说明 | +| --- | --- | --- | --- | +| `im_realname` | `realname_provider` | `aliyun_cloudauth` | 当前供应商 | +| `im_realname` | `aliyun_region_id` | `cn-hangzhou` | 阿里云地域 | +| `im_realname` | `aliyun_endpoint` | `cloudauth.aliyuncs.com` | OpenAPI Endpoint,按实际产品确认 | +| `im_realname` | `aliyun_access_key_id` | 空 | RAM AccessKey ID,禁止下发客户端 | +| `im_realname` | `aliyun_access_key_secret` | 空 | RAM AccessKey Secret,后台脱敏展示 | +| `im_realname` | `aliyun_scene_id` | 空 | 实人认证场景 ID,控制台创建 | +| `im_realname` | `aliyun_product_code` | 空 | 如产品要求传 product code,则配置 | +| `im_realname` | `callback_secret` | 空 | IM Server 校验阿里云回调的本地密钥 | +| `im_realname` | `result_query_interval_seconds` | `10` | 异步查询间隔 | +| `im_realname` | `result_query_max_attempts` | `30` | 最大查询次数 | +| `im_realname` | `mock_enabled` | `0` | 本地/测试环境 mock 开关 | + +AccessKey 建议使用最小权限 RAM 子账号,只授权实人认证相关 OpenAPI。生产环境如果后续接入 KMS/Secrets Manager,`hg_sys_config` 中可只保存密钥引用。 + +租户审核策略建议扩展 `hotgo.im_tenant_config`: + +| 字段 | 默认值 | 说明 | +| --- | --- | --- | +| `realname_review_policy` | `0` | 审核方式:`0=不审核`,`1=自动审核;自动审核失败/异常/不确定时进入人工审核` | +| `realname_provider_mode` | `id_card_2factor` | 第三方核验模式:`id_card_2factor` 或 `smart_verify` | +| `realname_required` | `0` | 是否强制实名,影响 `bootstrap.security.realname.required` 和受限写操作 | +| `realname_allow_skip` | `1` | 未实名提醒是否允许跳过 | +| `realname_remind_after` | `24h` | 客户端实名提醒间隔 | + +执行规则: + +- 租户 `realname_review_policy=0` 时,不做第三方审核,提交实名后写入 `pending`,等待后台人工通过或拒绝。 +- 租户 `realname_review_policy=1` 时,IM Server 自动调用阿里云;通过则写 `verified`,失败、异常或结果不确定则写 `manual_review`,等待后台人工复核。 +- 阿里云全局配置缺失时,即使租户开启自动审核,也不得阻断系统启动;提交时返回稳定错误,或降级写入 `manual_review` 交给人工审核。 + +### 3.2 推荐状态机 + +本地实名状态建议统一为: + +| 状态 | 含义 | 前端展示 | +| --- | --- | --- | +| `unverified` | 未提交或无有效记录 | 可提交 | +| `pending` | 已创建本地申请,等待第三方或人工结果 | 审核中 | +| `processing` | 第三方认证流程已开始,等待客户端完成或服务端查询 | 认证中 | +| `verified` | 第三方或人工审核通过 | 已实名 | +| `rejected` | 第三方或人工审核拒绝 | 未通过,可重新提交或申诉 | +| `manual_review` | 第三方结果不确定,需要后台人工处理 | 审核中 | +| `expired` | 客户端未在有效期完成认证 | 可重新提交 | +| `failed` | 第三方调用失败或系统异常 | 可重试 | + +对外接口可以继续兼容现有 `unverified|pending|verified|rejected`,把 `processing/manual_review` 展示为 `pending`,把 `expired/failed` 视租户策略映射为 `rejected` 或允许重新提交。 + +## 4. 两种接入流程 + +### 4.1 二要素核验流程 + +适合当前页面只提交姓名和身份证号的产品形态。 + +1. 客户端调用 `POST /v1/im/security/realname/submissions`,提交姓名、证件类型、证件号。 +2. IM Server 完成本地格式校验、幂等校验、重复 pending/verified 校验。 +3. IM Server 创建本地 `im_realname_verifications`,状态为 `pending`。 +4. 如果租户 `realname_review_policy=0`,流程到此结束,等待后台人工审核。 +5. 如果租户 `realname_review_policy=1`,IM Server 调用阿里云二要素核验 API。 +6. 阿里云返回一致/不一致/无法验证。 +7. IM Server 将本地状态更新为: + - 一致:`verified`,写入 `verified_at`。 + - 不一致:进入 `manual_review`,由后台人工复核。 + - 无法验证或供应商异常:进入 `manual_review`,由后台人工复核。 +8. 客户端继续调用 `GET /v1/im/security/realname` 获取最终状态。 +9. 如接入系统通知,服务端投递 `security.realname.updated`。 + +这个模式对前端改动最小,但实名强度只覆盖姓名和证件号一致性。 + +### 4.2 增强版实人认证流程 + +适合要求活体、人脸和证件一致性校验的场景。 + +1. 客户端调用 `POST /v1/im/security/realname/submissions`,提交姓名、证件类型、证件号,并附带阿里云客户端 SDK 要求的 `meta_info`。 +2. IM Server 创建本地申请,状态为 `pending`。 +3. IM Server 调用阿里云发起认证接口,例如 `InitSmartVerify` 或当前产品对应的初始化接口。 +4. 阿里云返回 `CertifyId`、认证 URL、认证 Token 或 SDK 所需参数。 +5. IM Server 保存 `provider_task_id=CertifyId`,并把客户端需要的非敏感参数返回给前端。 +6. 客户端调起阿里云 SDK、H5 或认证 URL 完成活体核身。 +7. IM Server 通过以下任一方式拿最终结果: + - 阿里云回调 IM Server。 + - IM Server 定时调用结果查询接口,例如 `DescribeSmartVerify` 或当前产品对应的查询接口。 +8. IM Server 根据结果更新本地状态为 `verified`、`rejected`、`manual_review`、`expired` 或 `failed`。 +9. 客户端轮询 `GET /v1/im/security/realname` 或接收系统通知刷新页面。 + +增强版模式下,客户端仍然不需要知道 AccessKey、AccessKeySecret、回调密钥等服务端凭据。客户端只使用后端返回的 `certify_id`、`certify_url`、`token`、`expires_at` 等短期认证参数。 + +## 5. 前后端接口建议 + +### 5.1 提交实名信息 + +保持现有接口不变: + +`POST /v1/im/security/realname/submissions` + +二要素模式请求: + +```json +{ + "name": "张三", + "id_type": "cn_id", + "id_number": "110101199001011234" +} +``` + +增强版模式请求可扩展: + +```json +{ + "name": "张三", + "id_type": "cn_id", + "id_number": "110101199001011234", + "meta_info": "{\"zimVer\":\"...\"}", + "return_url": "meetingim://realname/callback" +} +``` + +二要素模式响应: + +```json +{ + "submission_id": "rn_xxx", + "status": "verified", + "can_submit": false +} +``` + +增强版模式响应: + +```json +{ + "submission_id": "rn_xxx", + "status": "processing", + "provider": "aliyun_cloudauth", + "mode": "smart_verify", + "certify_id": "xxx", + "certify_url": "https://...", + "token": "短期认证参数,如产品返回", + "expires_at": "2026-09-20T10:00:00Z" +} +``` + +### 5.2 查询实名状态 + +继续使用: + +`GET /v1/im/security/realname` + +建议扩展字段: + +```json +{ + "status": "pending", + "display_status": "pending", + "provider": "aliyun_cloudauth", + "mode": "smart_verify", + "submission_id": "rn_xxx", + "masked_name": "张*", + "masked_id_number": "110***********1234", + "can_submit": false, + "review_reason": "", + "verified_at": null, + "next_action": "wait" +} +``` + +`next_action` 建议值: + +| 值 | 含义 | +| --- | --- | +| `submit` | 可提交 | +| `continue_provider_verify` | 需要继续完成阿里云认证 | +| `wait` | 等待结果 | +| `appeal` | 可申诉 | +| `none` | 无需操作 | + +### 5.3 阿里云回调接口 + +建议新增: + +`POST /v1/im/security/realname/providers/aliyun/callback` + +要求: + +- 只接收阿里云回调。 +- 校验签名、时间戳、nonce 或本地 `callback_secret`。 +- 根据 `CertifyId/provider_task_id` 幂等更新状态。 +- 原始 payload 写入事件表,敏感字段脱敏或加密。 + +这个接口不直接暴露给客户端。 + +## 6. 数据库改造建议 + +### 6.1 扩展实名主表 + +建议在 `im_realname_verifications` 增加第三方状态字段: + +```sql +ALTER TABLE im_realname_verifications + ADD COLUMN provider VARCHAR(32) NOT NULL DEFAULT '' AFTER status, + ADD COLUMN provider_mode VARCHAR(32) NOT NULL DEFAULT '' AFTER provider, + ADD COLUMN provider_scene_id VARCHAR(64) NOT NULL DEFAULT '' AFTER provider_mode, + ADD COLUMN provider_task_id VARCHAR(128) NOT NULL DEFAULT '' AFTER provider_scene_id, + ADD COLUMN provider_request_id VARCHAR(128) NOT NULL DEFAULT '' AFTER provider_task_id, + ADD COLUMN provider_status VARCHAR(32) NOT NULL DEFAULT '' AFTER provider_request_id, + ADD COLUMN provider_error_code VARCHAR(64) NOT NULL DEFAULT '' AFTER provider_status, + ADD COLUMN provider_error_message VARCHAR(256) NOT NULL DEFAULT '' AFTER provider_error_code, + ADD COLUMN provider_result_json JSON NULL AFTER provider_error_message, + ADD COLUMN submitted_at DATETIME(3) NULL AFTER provider_result_json, + ADD COLUMN callback_at DATETIME(3) NULL AFTER submitted_at, + ADD COLUMN expires_at DATETIME(3) NULL AFTER callback_at, + ADD COLUMN manual_review_required TINYINT(1) NOT NULL DEFAULT 0 AFTER expires_at, + ADD COLUMN version BIGINT NOT NULL DEFAULT 1 AFTER manual_review_required, + ADD INDEX idx_im_realname_provider_task (provider, provider_task_id), + ADD INDEX idx_im_realname_status_updated (status, updated_at); +``` + +注意:当前项目已经改为“一个租户一个库”,租户业务表中不再新增 `tenant_id` 字段。 + +### 6.2 新增第三方事件表 + +建议新增 `im_realname_provider_events`,用于记录回调、查询结果和异常响应,便于排查和审计: + +```sql +CREATE TABLE IF NOT EXISTS im_realname_provider_events ( + id BIGINT NOT NULL AUTO_INCREMENT, + submission_id VARCHAR(64) NOT NULL, + user_id BIGINT NOT NULL, + provider VARCHAR(32) NOT NULL, + provider_task_id VARCHAR(128) NOT NULL DEFAULT '', + event_type VARCHAR(32) NOT NULL, + provider_status VARCHAR(32) NOT NULL DEFAULT '', + request_id VARCHAR(128) NOT NULL DEFAULT '', + signature_valid TINYINT(1) NOT NULL DEFAULT 0, + payload_json JSON NULL, + error_code VARCHAR(64) NOT NULL DEFAULT '', + error_message VARCHAR(256) NOT NULL DEFAULT '', + created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + PRIMARY KEY (id), + INDEX idx_im_realname_events_submission (submission_id, created_at), + INDEX idx_im_realname_events_provider_task (provider, provider_task_id, created_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +``` + +### 6.3 配置 SQL 建议 + +HotGo 控制库增加全局供应商配置项: + +```sql +INSERT INTO hg_sys_config + (`group`, `name`, `type`, `key`, `value`, `default_value`, `sort`, `remark`, `status`, `created_by`, `created_at`, `updated_at`) +SELECT 'im_realname', '实名认证 Provider', 'string', 'realname_provider', 'aliyun_cloudauth', 'aliyun_cloudauth', 11, '当前实名认证供应商', 1, 1, NOW(), NOW() +WHERE NOT EXISTS (SELECT 1 FROM hg_sys_config WHERE `group`='im_realname' AND `key`='realname_provider'); + +INSERT INTO hg_sys_config + (`group`, `name`, `type`, `key`, `value`, `default_value`, `sort`, `remark`, `status`, `created_by`, `created_at`, `updated_at`) +SELECT 'im_realname', '阿里云 RegionId', 'string', 'aliyun_region_id', 'cn-hangzhou', 'cn-hangzhou', 20, '阿里云实人认证地域', 1, 1, NOW(), NOW() +WHERE NOT EXISTS (SELECT 1 FROM hg_sys_config WHERE `group`='im_realname' AND `key`='aliyun_region_id'); + +INSERT INTO hg_sys_config + (`group`, `name`, `type`, `key`, `value`, `default_value`, `sort`, `remark`, `status`, `created_by`, `created_at`, `updated_at`) +SELECT 'im_realname', '阿里云 Endpoint', 'string', 'aliyun_endpoint', 'cloudauth.aliyuncs.com', 'cloudauth.aliyuncs.com', 21, '阿里云实人认证 OpenAPI Endpoint,按开通产品确认', 1, 1, NOW(), NOW() +WHERE NOT EXISTS (SELECT 1 FROM hg_sys_config WHERE `group`='im_realname' AND `key`='aliyun_endpoint'); + +INSERT INTO hg_sys_config + (`group`, `name`, `type`, `key`, `value`, `default_value`, `sort`, `remark`, `status`, `created_by`, `created_at`, `updated_at`) +SELECT 'im_realname', '阿里云 AccessKey ID', 'string', 'aliyun_access_key_id', '', '', 22, '敏感配置,禁止下发客户端', 1, 1, NOW(), NOW() +WHERE NOT EXISTS (SELECT 1 FROM hg_sys_config WHERE `group`='im_realname' AND `key`='aliyun_access_key_id'); + +INSERT INTO hg_sys_config + (`group`, `name`, `type`, `key`, `value`, `default_value`, `sort`, `remark`, `status`, `created_by`, `created_at`, `updated_at`) +SELECT 'im_realname', '阿里云 AccessKey Secret', 'string', 'aliyun_access_key_secret', '', '', 23, '敏感配置,后台脱敏展示,禁止下发客户端', 1, 1, NOW(), NOW() +WHERE NOT EXISTS (SELECT 1 FROM hg_sys_config WHERE `group`='im_realname' AND `key`='aliyun_access_key_secret'); + +INSERT INTO hg_sys_config + (`group`, `name`, `type`, `key`, `value`, `default_value`, `sort`, `remark`, `status`, `created_by`, `created_at`, `updated_at`) +SELECT 'im_realname', '阿里云认证场景 ID', 'string', 'aliyun_scene_id', '', '', 24, '增强版实人认证场景 ID', 1, 1, NOW(), NOW() +WHERE NOT EXISTS (SELECT 1 FROM hg_sys_config WHERE `group`='im_realname' AND `key`='aliyun_scene_id'); + +INSERT INTO hg_sys_config + (`group`, `name`, `type`, `key`, `value`, `default_value`, `sort`, `remark`, `status`, `created_by`, `created_at`, `updated_at`) +SELECT 'im_realname', '实名认证回调密钥', 'string', 'callback_secret', '', '', 30, 'IM Server 校验回调使用,禁止下发客户端', 1, 1, NOW(), NOW() +WHERE NOT EXISTS (SELECT 1 FROM hg_sys_config WHERE `group`='im_realname' AND `key`='callback_secret'); +``` + +HotGo 控制库增加租户级审核策略。以下写法用于现有库变更,正式发布时可合并到 `im_tenant_config` 建表 SQL: + +```sql +ALTER TABLE hotgo.im_tenant_config + ADD COLUMN realname_review_policy TINYINT(1) NOT NULL DEFAULT 0 COMMENT '实名审核策略:0不审核,1自动审核且失败转人工' AFTER max_group_members, + ADD COLUMN realname_provider_mode VARCHAR(32) NOT NULL DEFAULT 'id_card_2factor' COMMENT 'id_card_2factor 或 smart_verify' AFTER realname_review_policy, + ADD COLUMN realname_required TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否强制实名' AFTER realname_provider_mode, + ADD COLUMN realname_allow_skip TINYINT(1) NOT NULL DEFAULT 1 COMMENT '未实名提醒是否允许跳过' AFTER realname_required, + ADD COLUMN realname_remind_after VARCHAR(32) NOT NULL DEFAULT '24h' COMMENT '实名提醒间隔' AFTER realname_allow_skip; +``` + +默认值要求: + +- `realname_review_policy=0`,新租户默认不调用阿里云,也不自动通过;提交后进入 `pending`,等待后台人工审核。 +- `realname_review_policy=1`,自动调用阿里云;通过写 `verified`,失败、异常、不确定全部进入 `manual_review`。 +- 审核策略只影响当前租户,不影响其他租户。 + +## 7. 后端开发拆分 + +### 7.1 第一阶段:配置和状态机 + +1. 新增 `server/realname` 或 `server/identity` 包,封装实名服务。 +2. 从 HotGo `hg_sys_config` 读取 `im_realname` 分组供应商配置,并从 `im_tenant_config` 读取当前租户的审核策略。 +3. 扩展 `RealNameVerification` 类型和 MySQL adapter 字段。 +4. 新增状态更新方法,例如 `TenantRealNameUpdateProviderResult`。 +5. 新增 provider 事件表写入方法。 +6. 保持现有接口兼容,未配置第三方时仍可创建 `pending` 申请,不影响系统启动。 + +### 7.2 第二阶段:阿里云 Client + +1. 封装 `AliyunRealNameClient`,支持 mock client 和真实 client。 +2. 二要素模式实现:提交姓名、证件号,返回通过/拒绝/人工审核。 +3. 增强版模式实现: + - 发起认证:调用 `InitSmartVerify`、`InitFaceVerify` 或开通产品对应的初始化 API。 + - 查询结果:调用 `DescribeSmartVerify`、`DescribeFaceVerify` 或开通产品对应的查询 API。 +4. 记录阿里云 `RequestId`、`CertifyId`、错误码和错误消息。 + +### 7.3 第三阶段:回调和异步查询 + +1. 新增阿里云回调 Handler。 +2. 校验回调签名和时间窗口。 +3. 以 `provider_task_id` 幂等更新实名状态。 +4. 对没有回调或回调失败的任务,后台 worker 定时查询结果。 +5. 查询超过最大次数后进入 `manual_review` 或 `failed`。 + +### 7.4 第四阶段:后台审核和通知 + +1. 完善后台审核接口:通过、拒绝、申诉处理、审核备注。 +2. 实名通过/拒绝后发送系统通知。 +3. `GET /v1/im/bootstrap` 和 `GET /v1/im/me` 统一读取真实实名状态。 +4. 接入审计日志,记录管理员和第三方状态变更。 + +## 8. 安全与合规要求 + +1. 客户端不接触阿里云 AccessKey、AccessKeySecret、回调密钥。 +2. IM Server 普通日志不得打印姓名、身份证号、阿里云完整返回 payload。 +3. 身份证号只保存哈希、脱敏值和必要保护存储值;如必须保存原文,应使用 KMS 或应用层加密,并设置严格读取权限。 +4. `provider_result_json` 和事件表 payload 需要脱敏或加密,禁止存储身份证原文、人脸图片、活体照片。 +5. 回调接口必须校验签名、时间戳和幂等,避免伪造通过。 +6. AccessKey 使用最小权限 RAM 子账号,建议按环境隔离。 +7. 后台查询接口需要按 `security_auditor` 等角色授权,敏感字段默认脱敏。 +8. 实名通过后的状态变更需要写审计日志,便于追踪。 + +## 9. 异常处理策略 + +| 场景 | 建议处理 | +| --- | --- | +| 阿里云配置缺失 | 接口返回稳定错误 `REALNAME_PROVIDER_NOT_CONFIGURED`,或按配置退回人工 `pending` | +| 阿里云超时 | 本地状态保持 `pending/processing`,后台 worker 重试 | +| 阿里云明确不一致 | 状态改为 `rejected`,返回脱敏驳回原因 | +| 阿里云结果不确定 | 状态改为 `manual_review` | +| 用户重复提交 | 如果已有 `pending/processing/verified`,直接返回最新申请 | +| 用户认证中断 | 到期后改为 `expired`,允许重新提交 | +| 回调重复到达 | 根据 `provider_task_id + provider_status` 幂等忽略 | + +## 10. 客户端改动点 + +1. 继续使用当前实名提交和查询接口。 +2. 如果提交响应包含 `certify_url` 或 `token`,客户端调起阿里云 SDK/H5 认证流程。 +3. 认证完成后回到 IM 页面,调用 `GET /v1/im/security/realname` 刷新状态。 +4. 客户端不缓存原始姓名和证件号,只展示后端返回的脱敏值。 +5. 对 `processing/pending/manual_review` 统一展示“审核中/认证中”。 +6. 对 `rejected/expired/failed` 根据 `can_submit` 和 `next_action` 决定是否允许重新提交或申诉。 + +## 11. 建议验收点 + +1. 未配置阿里云时,IM Server 能正常启动,现有实名提交仍可落 `pending`。 +2. 配置 mock provider 后,提交实名可以覆盖通过、拒绝、异常和超时场景。 +3. 二要素模式下,提交后能自动更新为 `verified/rejected`。 +4. 增强版模式下,提交后返回 `certify_id/certify_url`,完成认证后状态能回写。 +5. 重复 `Idempotency-Key` 不产生重复申请。 +6. 阿里云回调重复投递不重复更新、不重复通知。 +7. 日志、系统通知、Push、搜索索引中均不出现身份证原文。 +8. 后台能按状态查看实名申请,并能人工处理 `manual_review` 和申诉。 + +## 12. 参考文档 + +- 阿里云 OpenAPI 开发者门户:`https://api.aliyun.com/` +- 阿里云实人认证服务 API:`https://api.aliyun.com/product/Cloudauth` +- `InitSmartVerify`:发起增强版认证请求,返回认证请求 ID。 +- `DescribeSmartVerify`:查询增强版认证结果。 +- `InitFaceVerify` / `DescribeFaceVerify`:按开通产品和接入形态选择的实人认证初始化与结果查询接口。 + +最终使用哪个 API 名称和参数,以实际开通的阿里云产品、地域、控制台场景配置和 API 门户为准。 diff --git a/docs/third/IM-阿里云实名认证租户配置字段SQL.sql b/docs/third/IM-阿里云实名认证租户配置字段SQL.sql new file mode 100644 index 0000000..7509a04 --- /dev/null +++ b/docs/third/IM-阿里云实名认证租户配置字段SQL.sql @@ -0,0 +1,13 @@ +-- IM 阿里云实名认证:租户级审核策略字段 +-- 执行库:hotgo +-- 说明: +-- 1. realname_review_policy=0:不自动审核,实名提交进入 pending,等待后台人工审核。 +-- 2. realname_review_policy=1:自动调用阿里云审核,通过写 verified;失败、异常、不确定写 manual_review,等待人工复核。 +-- 3. realname_provider_mode 只表示阿里云核验模式,不承担审核开关含义。 + +ALTER TABLE hotgo.im_tenant_config + ADD COLUMN realname_review_policy TINYINT(1) NOT NULL DEFAULT 0 COMMENT '实名审核策略:0不审核,1自动审核且失败转人工' AFTER max_group_members, + ADD COLUMN realname_provider_mode VARCHAR(32) NOT NULL DEFAULT 'id_card_2factor' COMMENT 'id_card_2factor 或 smart_verify' AFTER realname_review_policy, + ADD COLUMN realname_required TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否强制实名' AFTER realname_provider_mode, + ADD COLUMN realname_allow_skip TINYINT(1) NOT NULL DEFAULT 1 COMMENT '未实名提醒是否允许跳过' AFTER realname_required, + ADD COLUMN realname_remind_after VARCHAR(32) NOT NULL DEFAULT '24h' COMMENT '实名提醒间隔' AFTER realname_allow_skip;