HarmonyOS 私密相册实战:一张照片移入保险箱后,怎样让普通相册真的看不见

HarmonyOS 私密相册实战:一张照片移入保险箱后,怎样让普通相册真的看不见

真机环境:ALN-AL80,OpenHarmony 7.0.0.32(Beta2),API 26。验证覆盖移入保险箱、系统身份认证、二级密码解锁、普通相册隔离和恢复原相册。

私密相册不只是多一个带锁页面

很多私密相册 Demo 只做了一个密码页:输入正确就跳到另一组图片。真正接入相册后,风险往往出现在入口之外------首页缩略图、搜索结果、地图回忆、最近照片和视频选片都可能继续显示同一条记录;应用切到后台时,任务缩略图还可能保留私密画面。

双镜记忆相机的目标是把一张公开照片移入保险箱后,普通相册立刻不再展示;进入保险箱必须经过系统身份认证或本地二级密码;离开保险箱再次锁定;恢复照片后,它才重新回到普通相册。

本次使用一张虚构成年旅行者公开测试图,完整走一遍"公开---私密---解锁---隐藏---恢复---重新出现"的状态变化。

先划清实现的安全边界

User Authentication Kit 官方能力页明确支持锁屏密码、人脸和指纹认证,并把访问隐私相册列为用户操作授权场景;认证凭据由系统安全隔离环境存储和处理。官方同时允许生物认证不方便时切换到业务自定义认证方式。

二级密码使用 HarmonyOS SDK 中的 Asset Store Kit 保存。Asset Store Kit 面向密码、Token 等短敏感数据,适合保存应用自己的 6 位解锁凭据。

当前实现的边界分为四层:

  1. 相册记录的 visibility 决定公开或私密,所有公开入口统一过滤;
  2. User Authentication Kit 负责系统人脸、指纹和锁屏密码认证;
  3. Asset Store Kit 保存应用二级密码,作为可控的备用入口;
  4. 私密照片详情开启窗口隐私模式,离开保险箱立即锁定。

照片文件位于应用沙箱,本文验证的是应用内可见性、访问授权和窗口保护。若业务需要独立文件加密、密钥轮换或跨设备密文同步,还要在文件层增加专门的加密方案。

第一步:从真实照片详情移入保险箱

操作前,人物照片位于普通相册详情,页面可以直接看到"移入保险箱"入口。

移动操作不复制第二份数据库记录,而是更新同一条记录的可见性并持久化:

ts 复制代码
private async moveRecordToVault(recordId: string): Promise<void> {
  const wasViewingDetail = this.galleryViewMode === 'detail';
  await this.updateRecordVisibility(recordId, 'private');
  this.vaultSelectedId = recordId;
  this.vaultUnlocked = false;
  if (wasViewingDetail) {
    this.galleryViewMode = 'album';
    this.switchTab('vault');
    this.vaultStatusText = '照片已移入保险箱,验证身份后可查看';
  }
}

updateRecordVisibility() 同步更新相册记录、地图标记和持久化数据。状态写入成功后才关闭详情,避免页面先消失而写盘失败。

图 1:真机提示照片已经移入保险箱,当前页面仍处于锁定状态。

这一步完成了数据归类,没有自动解锁保险箱。即使操作来自照片详情,查看私密内容仍需要重新验证身份。

第二步:优先调用系统身份认证

系统认证同时请求 PIN、人脸和指纹三种本地认证类型,并为本次认证生成 16 字节随机 challenge。不同设备支持的安全等级可能不同,因此从 ATL3 依次回退到 ATL2、ATL1;只有返回"不支持当前等级"或"不支持认证类型"时才继续回退,普通认证失败不会被当作降级理由。

ts 复制代码
const authParam: userAuth.AuthParam = {
  challenge: createRandomChallenge(),
  authType: [
    userAuth.UserAuthType.PIN,
    userAuth.UserAuthType.FACE,
    userAuth.UserAuthType.FINGERPRINT
  ],
  authTrustLevel: userAuth.AuthTrustLevel.ATL3
};

const instance = userAuth.getUserAuthInstance(
  authParam,
  { title: '验证身份' }
);
instance.on('result', callback);
instance.start();

真机固定在测试支架上,前置摄像头没有正对已录入人脸,本次系统认证明确返回"未识别成功"。系统面板同时给出指纹和锁屏密码出口。

图 2:真机 User Authentication Kit 返回人脸未识别成功,并提供指纹和锁屏密码入口。

认证失败不会打开保险箱。页面保留锁定状态,用户可以重试系统认证,也可以使用应用的二级密码。

第三步:用 Asset Store Kit 保存二级密码

首次使用二级密码时,页面要求输入并确认 6 位数字。截图中的输入内容已经由密码框隐藏。

图 3:系统认证失败后进入二级密码设置,密码内容不显示在页面和文章中。

保存时不使用 Preferences 明文写入。Asset Store 条目设置为设备解锁后可访问、要求设备已经设置锁屏密码、禁止同步到其他设备:

ts 复制代码
const attributes: asset.AssetMap = new Map();
attributes.set(asset.Tag.SECRET, toBytes(pin));
attributes.set(asset.Tag.ALIAS, toBytes('vault.pin.v1'));
attributes.set(asset.Tag.ACCESSIBILITY, asset.Accessibility.DEVICE_UNLOCKED);
attributes.set(asset.Tag.REQUIRE_PASSWORD_SET, true);
attributes.set(asset.Tag.SYNC_TYPE, asset.SyncType.NEVER);
attributes.set(
  asset.Tag.CONFLICT_RESOLUTION,
  asset.ConflictResolution.OVERWRITE
);
await asset.add(attributes);

验证时按别名查询唯一条目,并使用固定时间比较,避免在第一位不同时过早返回:

ts 复制代码
function constantTimeEqual(left: Uint8Array, right: Uint8Array): boolean {
  let difference = left.length ^ right.length;
  const length = Math.max(left.length, right.length);
  for (let index = 0; index < length; index++) {
    difference |= (left[index] ?? 0) ^ (right[index] ?? 0);
  }
  return difference === 0;
}

二级密码保存成功后,保险箱在当前会话中解锁,刚刚移入的照片作为唯一私密记录出现。

图 4:真机保险箱已经解锁,私密照片数量为 1。

第四步:所有普通入口统一过滤私密记录

相册首页不能只在 UI 层隐藏某一张卡片。双镜记忆相机先从记录集合生成公开视图,再把这份集合交给相册、语义搜索、地图、最近照片和视频选片。

ts 复制代码
const publicRecords = records.filter(
  record => record.visibility !== 'private'
);
const vaultRecords = records.filter(
  record => record.visibility === 'private'
);

语义搜索返回图片路径后还会再次查找公开记录。这样即使系统索引尚未完成删除,私密照片也不会绕过当前业务权限重新出现在结果页。

从保险箱返回普通相册后,测试照片已经从公开记录集合中消失,相册计数同步减少一张。

页面离开保险箱时还会执行 lockVault(),清空当前选择并关闭私密详情。再次进入必须重新认证,不能依赖上一次解锁状态长期驻留。

第五步:窗口隐私模式保护任务缩略图

打开私密照片详情时,应用为当前窗口打开隐私模式:

ts 复制代码
const currentWindow = await window.getLastWindow(context);
await currentWindow.setWindowPrivacyMode(true);

关闭私密详情时恢复普通窗口状态;离开保险箱则同时关闭详情并重新锁定。窗口隐私模式负责系统截屏和任务缩略图层面的保护,业务记录过滤负责应用内部入口,两者解决的是不同问题,不能互相替代。

第六步:恢复照片并确认重新出现

在已解锁保险箱中点击"恢复到相册",同一条记录的 visibility 改回 public。写入成功后,页面提示"已恢复到相册"。

图 5:恢复操作已经完成,保险箱中的私密记录被移回公开集合。

返回相册后,人物照片重新进入公开记录集合,相册数量也恢复到移动前的值。至此,公开---私密---公开的状态闭环完成。

真机过程里最值得保留的两个细节

系统认证失败也必须有完整路径

固定支架导致人脸识别失败,正好验证了异常路径:系统面板没有被应用绕过,保险箱保持锁定;用户仍可选择指纹、锁屏密码或应用二级密码。真实产品不能只验证"人脸一次成功"的理想情况。

"看不见"要覆盖所有数据消费者

只修改相册首页会留下搜索、地图、最近照片和成片选片等旁路。把公开/私密过滤放到记录层,所有功能域得到的都是已经过权限判断的集合;搜索结果还要在跳转详情前重新检查可见性。

最终效果

这次 ALN-AL80 真机验证完成了完整状态闭环:公开照片进入保险箱;保险箱保持锁定;系统人脸认证失败且没有放行;二级密码安全保存并成功解锁;私密照片在保险箱可见;普通记录集合不再包含这张照片;执行恢复操作后,照片重新回到公开记录集合。

User Authentication Kit 提供可信的系统认证界面,Asset Store Kit 保存应用备用凭据,记录层控制全部业务入口的可见性,窗口隐私模式保护私密照片详情。四层组合以后,保险箱不再只是一个带密码的页面,而是从数据访问到系统窗口都能保持一致状态的完整功能。

相关推荐
小雨青年2 小时前
【HarmonyOS 7 悬浮页签深度实战】01 HdsTabs、Tabs 与悬浮页签如何选择
华为·harmonyos
kiros_wang7 小时前
@Observed/@ObjectLink深层监听:嵌套对象更新失效根源与修复
华为·harmonyos
智塑未来13 小时前
鸿蒙6.1隐私安全升级——加密分享限时查看+水印+应用锁+防诈一键开启
安全·华为·harmonyos
OH_TPC14 小时前
HarmonyOS APP开发---“股动力“实时行情App,需要用到这个库
华为·音视频·harmonyos·鸿蒙
小雨青年17 小时前
【HarmonyOS 7 沉浸光感深度实战】10 通用组件封装与现有项目接入
华为·harmonyos
YM52e17 小时前
蒙特卡洛模拟器:鸿蒙 Canvas 上的大数定律与中心极限定理实证
华为·harmonyos
Dovis(誓平步青云)18 小时前
DevEco Studio 6.1.1 Windows 安装实录:从下载校验到首次启动
android·开发语言·数据库·人工智能·windows·harmonyos
2501_9197490318 小时前
华为鸿蒙免费排班APP—小羊排班
华为·harmonyos·鸿蒙
大雷神19 小时前
相册照片不用上传:HarmonyOS Core Vision 端侧识别动物与植物
harmonyos