【HarmonyOS 7新能力036】分布式数字身份工程封装把接入逻辑放进可维护的分层结构数字身份场景常见的错误是为了证明一个简单事实而传递整份身份资料只需要确认“已成年”却上传姓名、证件号码和完整出生日期。分布式数字身份真正值得关注的工程价值是让用户持有凭证并在明确用途下只披露完成业务所需的声明。要实现这一点项目不能把凭证读取、授权弹窗、证明生成和业务放行揉在一个页面中。说明本文使用的IdentityOrchestrator、CredentialPort等均为教学抽象不对应某个 HarmonyOS SDK 的真实类名或接口签名。DID、可验证凭证、密码学套件、设备支持及合规要求请以当前官方文档、目标 SDK 与实际业务所在地规则为准。1. 身份验证的输入应是声明而不是证件业务首先描述自己需要验证的事实例如年龄满足范围、某资质有效、会员状态成立。请求中要包含用途、接收方、有效时间和是否允许替代声明而不是预设必须取出哪张证件的哪些字段。export interface ClaimRequest { requestId: string verifierId: string purpose: string claims: readonly ClaimRequirement[] expiresAt: number nonce: string } export interface ClaimRequirement { type: string predicate?: string required: boolean }这种建模把业务目标与凭证格式分开。未来更换凭证发行方或证明协议时业务页面仍然只关心声明是否满足。2. 四层架构约束数据流向业务页面展示用途并接收验证结果身份编排层校验请求、组织授权和选择凭证能力适配层隔离钱包、证明与验证接口凭证仓库管理索引、状态和受保护存储。上层只依赖抽象能力凭证原文不应穿过所有层级。export interface CredentialPort { findCandidates(requirements: readonly ClaimRequirement[]): PromiseCredentialSummary[] createPresentation(input: PresentationInput): PromisePresentationResult } export interface VerificationPort { verify(presentation: string, request: ClaimRequest): PromiseVerifyResult }端口应保持最小避免把底层 SDK 对象直接返回页面。平台升级只修改适配器授权规则和业务结果映射则留在编排层。3. 请求进入后先做真实性和时效校验应用不能因为请求长得像 JSON 就信任它。编排器应检查接收方标识、用途是否为空、时间窗口、随机数、声明白名单和请求完整性。来自二维码、网页或跨设备的输入都视为不可信边界。export function validateRequest(req: ClaimRequest, now: number): ValidationResult { if (!req.requestId || !req.verifierId || !req.purpose) return invalid(MISSING_CONTEXT) if (req.expiresAt now) return invalid(REQUEST_EXPIRED) if (!req.nonce || req.claims.length 0) return invalid(INVALID_CHALLENGE) if (req.claims.some(item !claimAllowList.has(item.type))) return invalid(CLAIM_NOT_ALLOWED) return valid() }随机数与请求 ID 用于防止旧证明被简单复用。具体签名校验与信任链必须调用经过验证的平台或密码学实现不能自创算法。4. 用户授权必须具体、可理解“是否同意授权”信息量不足。授权页需要清楚展示接收方、用途、这一次要证明的声明、是否会传出原始属性、有效期限和拒绝后的影响。可选声明默认不勾选用户选择只对本次请求生效。export interface ConsentViewModel { verifierName: string purpose: string disclosures: readonly DisclosureItem[] expiresText: string canDeny: boolean }授权记录与凭证内容分离只记请求摘要、选择项和时间。不能把一次授权扩展为长期通用授权也不能用拒绝后无限弹窗迫使用户接受。5. 候选凭证选择遵守最小披露同一声明可能由多张凭证满足。选择器应优先考虑披露属性更少、有效期合适、来源受信且状态可确认的候选而不是简单拿第一张。算法输出选择理由便于授权页解释和测试。export function rankCandidate(c: CredentialSummary, req: ClaimRequest): number { const extraPenalty c.availableClaims.filter(x !requested(req, x)).length * 10 const expiryPenalty c.expiresSoon ? 20 : 0 const trustBonus c.issuerTrusted ? 30 : 0 return trustBonus - extraPenalty - expiryPenalty }示例权重仅说明方法不是通用标准。若协议支持谓词证明应优先证明结论若只能披露属性则明确告诉用户将发送什么。6. 凭证摘要与凭证材料分开列表页只需要名称、发行方、可用状态和到期提示不需要解密完整凭证。仓库返回摘要只有用户确认并进入证明生成时受控适配器才在最短生命周期内访问必要材料。export interface CredentialSummary { id: string displayName: string issuerName: string availableClaims: readonly string[] expiresAt: number issuerTrusted: boolean expiresSoon: boolean }UI 不缓存原始凭证不把材料写入日志、剪贴板或普通首选项。离开流程后清理临时对象系统截图和后台预览也要按产品安全策略处理。7. 证明生成必须绑定本次用途生成的证明应绑定请求随机数、验证方、用途和时效避免在另一个场景被重放。编排器构造严格输入适配器完成真实密码学操作业务层不得拼接签名或自行管理私钥。export interface PresentationInput { credentialId: string requestedClaims: readonly string[] verifierId: string purpose: string nonce: string expiresAt: number } export type PresentationResult | { status: created; payload: string } | { status: cancelled } | { status: failed; code: string }失败只向页面暴露可处理的归一化错误底层细节进入脱敏诊断。用户取消不是异常也不应自动重试。8. 验证结果要与业务决定解耦密码学验证通过只说明证明在当前规则下成立不代表业务一定要放行。业务还可能检查请求是否过期、声明是否完整、发行方是否符合场景策略以及凭证是否撤销。验证层输出事实策略层做决定。export interface VerifyResult { cryptographicallyValid: boolean claimsSatisfied: readonly string[] missingClaims: readonly string[] issuerStatus: trusted | unknown | blocked credentialStatus: active | revoked | unknown }unknown不能被偷偷转为通过。低风险场景可提示补充高风险场景可拒绝或转人工流程规则必须明确记录。9. 状态机避免重复授权与重放身份流程至少包含validating、selecting、consenting、presenting、verifying、completed、cancelled和failed。状态转换由编排器统一执行页面按钮只发送事件。export type IdentityFlowState | { kind: validating } | { kind: consenting; model: ConsentViewModel } | { kind: presenting; requestId: string } | { kind: completed; result: BusinessIdentityResult } | { kind: failed; code: string; retryable: boolean }同一个requestId在进行中时拒绝再次启动完成后保存短期消费标记。重复点击、页面旋转或返回前台不能触发第二次证明生成。10. 撤销、过期和离线边界凭证有生命周期。仓库在使用前检查本地到期状态适配器按官方机制确认撤销状态。若撤销信息必须联网而当前离线结果应为unknown由场景策略决定是否允许稍后验证。export interface CredentialStatusPolicy { requireFreshStatus: boolean maxStatusAgeMs: number allowOfflineUnknown: boolean }缓存撤销状态要带获取时间和来源不能永久复用。删除凭证、清除账号或撤销授权时应同步清理索引、临时证明和相关缓存但保留何种合规审计需单独定义。11. 测试聚焦隐私不变量除了正常流程还要测试过期请求、未知声明、用户拒绝、无匹配凭证、证明失败、验证方不可信、凭证撤销、离线未知和重复请求。更重要的是验证“不该发生的事”可选属性没有默认披露、日志没有凭证原文、页面拿不到私钥、拒绝后不会继续生成证明。it(discloses only approved claims, async () { const input buildPresentation([ageOver18]) const result await adapter.createPresentation(input) expect(result).not.toContain(fullBirthDate) expect(result).not.toContain(documentNumber) })真机验收还需覆盖系统返回、后台恢复、锁屏、设备时间变化和权限拒绝。协议互操作性应与真实验证端联调单元测试不能替代跨实现验证。12. 落地清单与总结落地前确认业务请求的是声明而非整份证件用途、接收方和时效可见请求经过真实性校验可选信息默认不披露凭证材料只在受控层短暂访问证明绑定本次随机数和验证方验证事实与业务决策分离撤销、过期、离线都有明确状态日志与缓存不含敏感原文用户可以拒绝和撤销。分布式数字身份的工程目标不是把传统证件电子化而是重新收紧身份数据的流动路径。通过声明驱动、明确授权、候选最小化、用途绑定和分层适配项目才能在能力演进时保持边界稳定让“只证明必要事实”成为代码能够持续验证的不变量。本文为 HarmonyOS 7 新能力工程实践系列第 036 篇。示例用于说明架构方法生产接入请依据官方文档、目标 SDK、协议规范和隐私合规评审执行。十三、接入前检查清单分布式数字身份工程封装正式接入前工程侧要过一遍检查清单SDK 版本与目标 API 版本匹配、权限在清单中声明且按需申请、能力开关在专用配置层管理、包体积增量符合预算。任何一项不过就先停带着不匹配的版本往上冲排错成本会翻倍。interface PrecheckItem { name: string; pass: boolean; note?: string } const checklist: PrecheckItem[] [ { name: SDK/API版本匹配, pass: false }, { name: 权限声明完整, pass: false }, { name: 开关集中管理, pass: false }, { name: 包体积增量预算, pass: false }, ] const allPass checklist.every(x x.pass)检查清单跑通的标志是新建工程按清单接入一次成功且不依赖任何口头经验。做不到说明文档还缺关键步骤。十四、常见坑与版本适配记录分布式数字身份工程封装最常踩的坑集中在三处示例代码用的旧接口在新版本被拆分、默认配置与生产预期不一致、调试环境与真机行为有差异。每踩一个坑就记进版本适配表注明坑点、影响版本、修复方式与验证方式。interface Pitfall { issue: string; sinceApi: string; fix: string; verifiedOn: string[] } const pitfalls: Pitfall[] [] function knownPit(api: string): Pitfall[] { return pitfalls.filter(p p.sinceApi api) }适配表的价值在升级时兑现每次目标版本变更先对照表过一遍已知坑能省下一整轮盲目排查。
阅读完成 · 觉得有帮助?