eID —— 管理控制台(运维面板)¶
面向运维人员与开发者的指南:eID 平台的管理控制台由 Next.js 运维面板
(admin/,端口 3001)与 Go 管理 API(/v3/admin/*)组合而成。运维人员通过它
管理公民、设备、证书、会话、KYC/DAN、组织(法人)、依赖方(RP)、审计日志与系统状态。
独立应用。 管理控制台是与
web/(面向公民的 RP 演示,端口 3000) 完全独立的 Next.js 应用。代码目录:admin/src/。
1. 管理范围¶
| 模块 | 能力 |
|---|---|
公民(/users) |
检索(etsi / РД / 姓名 / documentNumber)、详情 + 设备 + 证书 + 审计 |
设备(/devices) |
列表 / 检索、详情、停用遗失设备 |
证书(/certificates) |
列表 / 检索、状态、吊销 |
PKI(/pki、/org-ca) |
CA 状态、OCSP/CRL 路径、e-Seal 的 QSCD 状态 |
组织(/organizations) |
法人注册、代表人、签发 e-Seal 证书 |
RP(/rps) |
注册 / 编辑 / 停用 / 重新启用依赖方、轮换 secret、管理子系统 |
审批(/approvals) |
四眼原则:敏感操作需另一位管理员确认 |
审计(/audit) |
所有操作的日志 + CSV 导出(eIDAS/ISO 27001) |
管理员(/admins) |
创建 / 变更角色 / 停用管理员(仅 SUPER_ADMIN) |
系统(/system) |
版本、运行时长,以及配置 / HSM / KYC / PKI 的非敏感状态 |
2. 架构 —— BFF(浏览器永远看不到 secret)¶
管理控制台采用 Backend-for-Frontend(BFF) 模式。浏览器绝不会接触到管理会话令牌 ——
令牌仅保存在 Next 服务端的 httpOnly cookie 中。
浏览器 ──fetch──▶ Next BFF(端口 3001)──Bearer──▶ Go 管理 API(/v3/admin/*)
(cookie: /api/login、/api/mfa、 adminAuth 中间件
admin_session, /api/proxy/[...path] + requireCap(RBAC)
httpOnly)
- 客户端 → BFF。 页面调用
api("users?q=…")时,请求会发往/api/proxy/*路由 (admin/src/lib/client.ts)。 - BFF → Go。 代理路由从 cookie 中读取会话令牌,并以
Authorization: Bearer <token>转发至${ADMIN_BACKEND_URL}/v3/admin/<path>(admin/src/app/api/proxy/[...path]/route.ts)。 令牌不会暴露给浏览器,因此无法通过 XSS 窃取。 - 防护措施。 路径穿越白名单(
[A-Za-z0-9._-],禁止./..)、 对写操作请求校验 Origin/Referer 主机与host是否一致(在sameSite=strict之外的 CSRF 纵深防御),以及将上游 5xx 错误统一替换为通用提示。 - 认证守卫。
(app)layout 在服务端调用getMe(),若不存在有效会话则重定向到/login(admin/src/app/(app)/layout.tsx)。 - CSP / 安全响应头。
admin/src/middleware.ts—— nonce +strict-dynamic、connect-src 'self'、frame-ancestors 'none'、HSTS 等。
信任边界。 Go 侧通过
adminAuth中间件保护/v3/admin/*(server/internal/httpapi/server.go)。 优先级:(1)Authorization: Bearer <会话令牌>→ 完整 RBAC;(2)X-Admin-Key→ 应急(break-glass)SUPER_ADMIN(常数时间比较);(3) 仅在dev配置下, 当SMARTID_ADMIN_API_KEY为空时为开放的 SUPER_ADMIN。在 staging/prod 上 绝不允许 fail-open —— 必须提供 Bearer 或应急密钥。
3. 登录 + MFA¶
流程:邮箱/密码 → TOTP(MFA)→ 会话令牌。首次登录时,用户通过二维码绑定验证器应用。
| 步骤 | BFF 路由 | Go 接口 | 说明 |
|---|---|---|---|
| 1. 登录 | POST /api/login |
POST /v3/admin/auth/login |
校验邮箱/密码;成功后返回 mfa scope 的令牌 |
| 2. MFA | POST /api/mfa |
POST /v3/admin/auth/mfa |
TOTP 验证码 → session scope 的令牌 |
| 3. 登出 | POST /api/logout |
POST /v3/admin/auth/logout |
删除 cookie(令牌本身为无状态) |
- 密码。 通过
internal/admin/password.go哈希存储;错误提示统一为 “邮箱或密码不正确”,以防范时序攻击与账号枚举。 - TOTP(MFA)。 RFC 6238、HMAC-SHA1、30 秒步长、6 位数字 —— 自行实现,未引入外部库
(
server/internal/admin/totp.go)。 首次登录时服务端返回otpauth://URI 与密钥;登录页将其渲染为二维码, 供用户添加到 Google/Microsoft Authenticator。验证码允许 ±1 步长的窗口(时钟偏差), 并保存已使用的计数器以阻止重放(RFC 6238 §5.2)。 - 令牌。 使用 HMAC-SHA256 签名的无状态令牌(未使用 JWT 库),
格式为
<b64url(payload)>.<b64url(HMAC)>(server/internal/admin/token.go)。mfascope 约 5 分钟,sessionscope 约 1 小时。 - Cookie。
admin_mfa(5 分钟,等待 MFA)与admin_session(1 小时)—— 均为httpOnly、sameSite=strict,在生产环境还带secure。 - 限流。 登录(按 IP+邮箱)与 MFA(按 IP)均有节流;超出后返回 429 +
Retry-After。 - 修改密码。
POST /v3/admin/auth/password—— 已登录管理员修改自己的密码 (需确认当前密码)。无需额外 capability。
4. RBAC —— 角色与 capability¶
每个路由都声明所需的 capability;requireCap 中间件校验当前登录管理员的角色是否具备该
capability(最小权限原则)。权限矩阵见:
server/internal/admin/roles.go。
Capabilities: rp:read、rp:write、org:read、org:write、user:read、device:read、
device:revoke、cert:read、cert:revoke、session:read、audit:read、admin:manage、
config:read、config:write。
角色 → capability:
| 角色 | 授予的 capability |
|---|---|
SUPER_ADMIN |
全部 capability(含管理员管理) |
RP_OPERATOR |
rp:read rp:write org:read org:write user:read session:read audit:read |
SUPPORT |
rp:read org:read user:read device:read device:revoke cert:read session:read audit:read |
AUDITOR |
rp:read org:read user:read device:read cert:read session:read audit:read config:read |
SECURITY_OFFICER |
org:read user:read device:read device:revoke cert:read cert:revoke session:read audit:read config:read |
若 capability 不足,Go 侧返回 403(“该操作权限不足”)。前端导航会显示全部页面, 但未授权的操作会在后端被 403 拦截。
5. 核心能力与 /v3/admin/* 接口¶
全部位于 adminAuth 之后。所需 capability 一并列出。
公民 / 设备 / 证书 / 会话(读取)¶
| 方法 + 路径 | Cap | 说明 |
|---|---|---|
GET /users?q=&limit=&offset= |
user:read |
检索公民(etsi/РД/姓名);若无结果,则按 documentNumber 查找所有者 |
GET /users/{etsi} |
user:read |
公民 + 设备 + 证书 + 审计 |
GET /devices?q=&active= |
device:read |
检索设备 |
GET /devices/{documentNumber} |
device:read |
设备详情 |
GET /certificates?q=&status= |
cert:read |
检索证书 |
GET /users/{etsi}/certificates |
cert:read |
某公民的证书历史 |
GET /sessions/{sessionId} |
session:read |
会话的安全视图 |
绝不返回敏感材料。 视图函数(
userView/deviceView/certView/sessionView,handlers_admin_read.go) 会剔除 HSM 句柄、Enc(x_client)、Paillier 模数、会话 secret/token 以及体积庞大的 证书 base64,仅展示安全字段。
RP(依赖方)¶
| 方法 + 路径 | Cap | 说明 |
|---|---|---|
POST /relying-parties |
rp:write |
注册 RP —— API secret 仅在此处返回一次 |
GET /relying-parties、GET /relying-parties/{id} |
rp:read |
列表 / 详情 |
PATCH /relying-parties/{id} |
rp:write |
编辑 |
POST /relying-parties/{id}/deactivate | /reactivate |
rp:write |
停用 / 重新启用 |
POST /relying-parties/{id}/rotate-secret |
rp:write |
新 secret(同样仅此一次) |
RP 子系统。 子系统由 RP 自身自动注册(find-or-create);管理员只能 列出 / 重命名 / 停用 / 合并:
| 方法 + 路径 | Cap | 说明 |
|---|---|---|
GET /relying-parties/{id}/subsystems |
rp:read |
RP 内的子系统列表 |
PATCH /relying-parties/{id}/subsystems/{sid} |
rp:write |
修改子系统的显示名称 |
POST /relying-parties/{id}/subsystems/{sid}/deactivate | /activate |
rp:write |
停用 / 启用 |
POST /relying-parties/{id}/subsystems/{sid}/merge |
rp:write |
合并到另一个子系统 |
完整的 RP 集成指南:RP_INTEGRATION.md。 子系统模型 / 协议:RP_SUBSYSTEMS.md。
证书吊销 + PKI¶
| 方法 + 路径 | Cap | 说明 |
|---|---|---|
POST /certificates/{serial}/revoke |
cert:revoke |
按序列号吊销(OCSP/CRL + 状态 + 停用设备);{reason} 取值 0–10 |
POST /devices/{documentNumber}/deactivate |
device:revoke |
停用遗失设备(默认 reason=1 keyCompromise) |
GET /pki/status |
config:read |
CA 的 subject/序列号/有效期、吊销数量、OCSP/CRL URL、Org CA、e-Seal QSCD |
KYC / DAN¶
KYC 流程主要经由手机应用与 DAN 回调完成(/v3/kyc/*,不在管理控制台之下 ——
server.go 的 mountKyc)。
GET /v3/kyc/methods 返回已启用的 KYC 方式(dan / gsign / passport / citizenCard)。
系统接口 GET /v3/admin/system 中的 kyc 区块展示 KYC 提供方的非敏感状态。
组织(法人)¶
| 方法 + 路径 | Cap | 说明 |
|---|---|---|
GET /organizations?q= |
org:read |
列表 / 检索 |
GET /organizations/{etsi} |
org:read |
详情 + 代表人 |
POST /organizations |
org:write |
注册(PENDING 或直接 ACTIVE) |
PATCH /organizations/{etsi} |
org:write |
名称 / 状态(状态机) |
POST /organizations/{etsi}/representatives |
org:write |
添加代表人 |
DELETE /organizations/{etsi}/representatives/{id} |
org:write |
停用某项代表权 |
POST /organizations/{etsi}/seal-certificate |
org:write |
签发 e-Seal(NTRMN)证书 |
组织接入:ORG_ONBOARDING.md。
审计 + 统计¶
| 方法 + 路径 | Cap | 说明 |
|---|---|---|
GET /audit?type=&subject=&limit=&offset= |
audit:read |
审计日志(分页) |
GET /audit/export?type=&subject= |
audit:read |
CSV 导出(eIDAS/ISO 27001 报告) |
GET /stats |
audit:read |
控制台指标与趋势 |
系统 / HSM 状态(只读)¶
| 方法 + 路径 | Cap | 说明 |
|---|---|---|
GET /system |
config:read |
版本、运行时长,以及 security/kyc/pki/hsm/push 配置的非敏感状态 |
不暴露任何 secret。
adminSystemInfo(handlers_admin_system.go) 绝不会暴露密钥 / 密码 / API 密钥 —— 只标明“是否已配置” (例如swMasterKeyConfigured: true)。
管理员账户管理(仅 SUPER_ADMIN)¶
| 方法 + 路径 | Cap | 说明 |
|---|---|---|
GET /admins |
admin:manage |
管理员列表(不含敏感信息的视图) |
POST /admins |
admin:manage |
新建管理员(邮箱 + 至少 8 位密码 + 角色) |
PATCH /admins/{id} |
admin:manage |
变更角色 |
POST /admins/{id}/deactivate |
admin:manage |
停用(而非删除 —— 以保留审计记录) |
四眼原则审批流程¶
当 SMARTID_ADMIN_REQUIRE_4EYES=true 时,敏感操作(吊销证书、停用设备、停用 RP)
不会立即执行 —— 而是转为 PENDING 请求(返回 202),需由另一位管理员确认。
| 方法 + 路径 | Cap | 说明 |
|---|---|---|
GET /approvals?status= |
audit:read |
待处理请求 |
POST /approvals/{id}/approve |
(该操作对应的 capability,动态判定) | 批准 |
POST /approvals/{id}/reject |
(该操作对应的 capability,动态判定) | 驳回 |
审批人必须 (a) 拥有该操作对应的 capability(例如吊销证书 → cert:revoke),
且 (b) 与发起人不同 —— 试图批准自己的请求会被 ErrSelfApproval(403)阻止
(handlers_admin_approval.go)。
6. 运行¶
前端(管理控制台):
关键环境变量只有一个:ADMIN_BACKEND_URL(Go 后端地址,默认 http://localhost:8080)。
后端(Go 管理 API) —— 在 server/ 目录下运行(go run ./cmd/smartid)。
管理 / RBAC 相关环境变量(server/internal/config/config.go,均以 SMARTID_* 开头):
| 环境变量 | 说明 |
|---|---|
SMARTID_ADMIN_TOKEN_SECRET |
会话令牌的 HMAC 密钥(≥16 个字符)。为空时管理端认证被禁用 |
SMARTID_ADMIN_SEED_EMAIL |
首位 SUPER_ADMIN 的邮箱(启动时 seed,幂等) |
SMARTID_ADMIN_SEED_PASSWORD |
首位管理员的密码(仅用于 seed —— 之后必须修改) |
SMARTID_ADMIN_SEED_ROLE |
seed 角色(默认 SUPER_ADMIN) |
SMARTID_ADMIN_API_KEY |
应急 X-Admin-Key(绕过 RBAC 的 SUPER_ADMIN;生产环境可选) |
SMARTID_ADMIN_REQUIRE_4EYES |
敏感操作是否需要两名管理员审批(默认 false) |
dev 配置。 在
dev配置下,若SMARTID_ADMIN_API_KEY为空,/v3/admin/*将变为开放的 SUPER_ADMIN(无需 Bearer)—— 仅供本地开发使用。staging/prod 绝不会出现这种情况;必须配置SMARTID_ADMIN_TOKEN_SECRET+ seed 或应急密钥。
7. 链接¶
- RP 集成(RP_INTEGRATION.md) —— 接入依赖方
- 组织接入(ORG_ONBOARDING.md) —— 法人 / e-Seal
- PKI / CA 接入(PKI_CA_ONBOARDING.md) —— CA 证书链、OCSP/CRL
源码:
- Go 管理 API:
server/internal/httpapi/handlers_admin_*.go,路由:server/internal/httpapi/server.go - RBAC / 认证:
server/internal/admin/(roles.go、service.go、totp.go、token.go、password.go) - 配置:
server/internal/config/config.go - 前端:
admin/src/