news 2026/10/27 18:39:58

【HarmonyOS 7新能力|036】分布式数字身份工程封装:把接入逻辑放进可维护的分层结构

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【HarmonyOS 7新能力|036】分布式数字身份工程封装:把接入逻辑放进可维护的分层结构

【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[]): Promise<CredentialSummary[]> createPresentation(input: PresentationInput): Promise<PresentationResult> } export interface VerificationPort { verify(presentation: string, request: ClaimRequest): Promise<VerifyResult> }

端口应保持最小,避免把底层 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) }

适配表的价值在升级时兑现:每次目标版本变更,先对照表过一遍已知坑,能省下一整轮盲目排查。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/27 18:32:42

2000-2024年 上市公司-企业全要素生产率TFP数据(+代码+文献)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/27 18:31:50

2001-2024年 上市公司-供应链网络排名数据(xlsx)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/27 18:29:58

基于 Qwen Code Skills 实践构建自定义数据分析智能体

一、Qwen Code Skills Qwen Code 是一个面向开发者的智能编码助手&#xff0c;类似于 Claude Code&#xff0c;随着 Claude Code 推出 Skills 功能&#xff0c; Qwen Code 也迅速集成了相关功能&#xff0c;通过 Skills 可以将专业知识打包成可发现的功能。每个技能由一个 SKI…

作者头像 李华
网站建设 2026/10/27 18:28:52

YOLOv5 OBB旋转框训练实战:从DOTA标注到角度回归与部署

简介&#xff1a;面向目标检测开发者与计算机视觉学习者的 YOLOv5 OBB 旋转框训练演示工程&#xff0c;在 YOLOv5 上扩展倾斜框支持&#xff0c;用顶点与旋转角度描述树木、车辆等带方向目标&#xff0c;弥补轴对齐框定位不准的缺陷。压缩包共 583 个文件、约 445.58MB&#xf…

作者头像 李华