当前位置: 首页 > news >正文

【鸿蒙NEXT】鸿蒙里面类似iOS的Keychain——关键资产(@ohos.security.asset)实现设备唯一标识

前言

在iOS开发中Keychain 是一个非常安全的存储系统,用于保存敏感信息,如密码、证书、密钥等。与 NSUserDefaults 或文件系统不同,Keychain 提供了更高的安全性,因为它对数据进行了加密,并且只有经过授权的应用程序才能访问存储的数据。那么在鸿蒙里面对应的是什么呢?

1、关键资产(@ohos.security.asset)

在鸿蒙里面也有类似的东西,叫做关键资产(@ohos.security.asset),关键资产存储服务提供了用户短敏感数据的安全存储及管理能力。其中,短敏感数据可以是密码类(账号/密码)、Token类(应用凭据)、其他关键明文(如银行卡号)等长度较短的用户敏感数据。

从API version 11 开始支持

使用关键资产需要导入模块AssetStoreKit

import { asset } from '@kit.AssetStoreKit';

2、asset常用操作

version 11 开始支持,异步方法,如下

  1. asset.add:add(attributes: AssetMap): Promise,新增一条关键资产,使用Promise方式异步返回结果。

  2. asset.remove:removeSync(query: AssetMap): void,删除符合条件的一条或多条关键资产,使用异步方式。

  3. asset.update:update(query: AssetMap, attributesToUpdate: AssetMap): Promise,更新符合条件的一条关键资产,使用Promise方式异步返回结果。

  4. asset.query:query(query: AssetMap): Promise<Array>,查询一条或多条符合条件的关键资产。若查询需要用户认证的关键资产,则需要在本函数前调用asset.preQuery,在本函数后调用asset.postQuery,使用Promise回调异步返回结果。

  5. asset.preQuery:preQuery(query: AssetMap): Promise,查询的预处理,用于需要用户认证的关键资产。在用户认证成功后,应当随后调用asset.query、asset.postQuery。使用Promise方式异步返回结果。

  6. asset.postQuery:postQuery(handle: AssetMap): Promise,查询的后置处理,用于需要用户认证的关键资产。需与asset.preQuery函数成对出现。使用Promise方式异步返回结果。

    version 12 开始支持,同步方法,如下

  7. asset.addSync:新增一条关键资产,使用Promise方式同步步返回结果。

  8. asset.removeSync:removeSync(query: AssetMap): void,删除符合条件的一条或多条关键资产,使用同步方式。

  9. asset.addSync:新增一条关键资产,使用Promise方式同步步返回结果。

  10. asset.removeSync:removeSync(query: AssetMap): void,删除符合条件的一条或多条关键资产,使用同步方式。

  11. asset.updateSync:updateSync(query: AssetMap, attributesToUpdate: AssetMap): void,更新符合条件的一条关键资产,使用同步方式返回结果。

  12. asset.querySync:querySync(query: AssetMap): Array,查询一条或多条符合条件的关键资产。若查询需要用户认证的关键资产,则需要在本函数前调用asset.preQuerySync,在本函数后调用asset.postQuerySync,使用同步方式返回结果。

  13. asset.preQuerySync:preQuerySync(query: AssetMap): Uint8Array,查询的预处理,用于需要用户认证的关键资产。在用户认证成功后,应当随后调用asset.querySync、asset.postQuerySync。使用同步方式返回结果。

  14. asset.postQuerySync:postQuerySync(handle: AssetMap): void,查询的后置处理,用于需要用户认证的关键资产。需与asset.preQuerySync函数成对出现。使用同步方式返回结果。

关键资产需要使用到的系统能力: SystemCapability.Security.Asset

3、asset的封装使用

在iOS中使用Keychain 比较常见的功能是存储一个值作为设备唯一标识,那么asset也以此作为示例封装一个,刚好前阵子项目里面也使用了。我也封装了一个工具类hmDeviceTools

3.1 导入需要的头文件

import { util } from '@kit.ArkTS'
import { asset } from '@kit.AssetStoreKit';
import { BusinessError } from '@kit.BasicServicesKit';

3.2 封装工具类

hmDeviceTools类内容

export class hmDeviceTools {private static deviceIdCacheKey = "testdevice_id_cache_key" //testkeyprivate static deviceId = ""/*** * 判断字符串是否为空* @param property 被检测的字符串* @return Boolean*/static isEmpty(property?: string | null): Boolean {if (property == '' || property == null || property == undefined || property == 'undefined' ||property.length == 0) {return true}return false}/*** 获取设备id*/static getDeviceId() {let deviceId = hmDeviceTools.deviceId//如果内存缓存为空,则从AssetStore中读取if (hmDeviceTools.isEmpty(deviceId)) {deviceId = getAssetMap(hmDeviceTools.deviceIdCacheKey)}//如果AssetStore中未读取到,则随机生成32位随机码,然后缓存到AssetStore中if (hmDeviceTools.isEmpty(deviceId)) {deviceId = util.generateRandomUUID(true).replace(new RegExp('-', "gm"), '')deviceId = deviceId.slice(0,Math.min(10,deviceId.length))//可以确保不会超出字符串的长度。setAssetMap(hmDeviceTools.deviceIdCacheKey, deviceId)}hmDeviceTools.deviceId = deviceIdreturn deviceId}
}

getDeviceId函数里面,我是截取的10位,大家可以工具自己的具体业务来自行截取,或者使用使用generateRandomUUID返回的32位。

3.3 addSync 设置数据

既然有异步和同步可选,我当然是使用addSync同步来写了,后面的方法都是使用同步来实现。

    
/*** 设置数据* @param key  要查找的索引* @param value 需要存的值*/
function setAssetMap(key: string, value: string) {let attr: asset.AssetMap = new Map();let result: Booleanif (canIUse("SystemCapability.Security.Asset")) {// 关键资产别名,每条关键资产的唯一索引。// 类型为Uint8Array,长度为1-256字节。attr.set(asset.Tag.ALIAS, stringToArray(key));// 关键资产明文。// 类型为Uint8Array,长度为1-1024字节attr.set(asset.Tag.SECRET, stringToArray(value));// 关键资产同步类型>THIS_DEVICE只在本设备进行同步,如仅在本设备还原的备份场景。attr.set(asset.Tag.SYNC_TYPE, asset.SyncType.THIS_DEVICE);//枚举,新增关键资产时的冲突(如:别名相同)处理策略。OVERWRITE》抛出异常,由业务进行后续处理。// attr.set(asset.Tag.CONFLICT_RESOLUTION,asset.ConflictResolution.THROW_ERROR)// 在应用卸载时是否需要保留关键资产。// 需要权限: ohos.permission.STORE_PERSISTENT_DATA。// 类型为bool。// attr.set(asset.Tag.IS_PERSISTENT, true);//我项目里面没有使用就先注释了,后续有需要这个再打开,并且要设置对应权限}if (isHasKey(key)) {result = updateAssetMap(attr, attr);} else {try {asset.addSync(attr);result = true} catch (error) {let err = error as BusinessError;console.error(`Failed to add Asset. Code is ${err.code}, message is ${err.message}`);result = false}}}

3.4 querySync 获取数据

/*** 获取数据* @param key  要查找的索引* @returns string 表示操作的结果*/
function getAssetMap(key: string): string {if (canIUse("SystemCapability.Security.Asset")) {let query: asset.AssetMap = new Map();// 关键资产别名,每条关键资产的唯一索引。// 类型为Uint8Array,长度为1-256字节。query.set(asset.Tag.ALIAS, stringToArray(key));//  关键资产查询返回的结果类型。query.set(asset.Tag.RETURN_TYPE, asset.ReturnType.ALL);// query.set(asset.Tag.RETURN_TYPE, asset.ReturnType.ATTRIBUTES); // 此处表示仅返回关键资产属性,不包含关键资产明文try {let res: Array<asset.AssetMap> = asset.querySync(query);for (let i = 0; i < res.length; i++) {// parse the attribute.if (res[i] != null) {// parse the secret.let secret: Uint8Array = res[0].get(asset.Tag.SECRET) as Uint8Array;// parse uint8array to stringlet secretStr: string = arrayToString(secret);return secretStr;}}} catch (error) {let err = error as BusinessError;console.error(`Failed to query Asset. Code is ${err.code}, message is ${err.message}`);return "";}}return "";
}

3.4 querySync 查询key

/*** 判断key是否存在* @param key 要查找的索引* @returns Boolean 表示添加操作的结果*/
function isHasKey(key: string): Boolean {if (canIUse("SystemCapability.Security.Asset")) {let query: asset.AssetMap = new Map();// 关键资产别名,每条关键资产的唯一索引。// 类型为Uint8Array,长度为1-256字节。query.set(asset.Tag.ALIAS, stringToArray(key));//  关键资产查询返回的结果类型。query.set(asset.Tag.RETURN_TYPE, asset.ReturnType.ALL);const res = queryAssetMap(query);if (!res || res.length < 1) {return false;}return true;}return false;
}

3.5 querySync 查询数据

/**
* 查找数据
* @param key  要查找的索引
* @returns Array<asset.AssetMap> 表示添加操作的结果
*/
function queryAssetMap(query: asset.AssetMap): Array<asset.AssetMap> {const assetMaps: asset.AssetMap[] = [];try {if (canIUse("SystemCapability.Security.Asset")) {const res: asset.AssetMap[] = asset.querySync(query);return res;}return assetMaps;} catch (error) {const err = error as BusinessError;console.error(`Failed to query Asset. Code is ${err.code}, message is ${err.message}`);return assetMaps;}
}

3.6 updateSync 更新数据

/*** 查找数据* @param key  要查找的索引* @returns Array<asset.AssetMap> 表示添加操作的结果*/
function queryAssetMap(query: asset.AssetMap): Array<asset.AssetMap> {const assetMaps: asset.AssetMap[] = [];try {if (canIUse("SystemCapability.Security.Asset")) {const res: asset.AssetMap[] = asset.querySync(query);return res;}return assetMaps;} catch (error) {const err = error as BusinessError;console.error(`Failed to query Asset. Code is ${err.code}, message is ${err.message}`);return assetMaps;}
}

使用到的其他函数

function stringToArray(str: string): Uint8Array {let textEncoder = new util.TextEncoder();return textEncoder.encodeInto(str);
}function arrayToString(arr: Uint8Array): string {let textDecoder = util.TextDecoder.create('utf-8', { fatal: false, ignoreBOM: true });let decodeToStringOptions: util.DecodeToStringOptions = {stream: false}let str = textDecoder.decodeToString(arr, decodeToStringOptions);return str;
}

4、特别说明

如果需要卸载之后获取的值不变,需要设置IS_PERSISTENT属性,需要申请ohos.permission.STORE_PERSISTENT_DATA权限。

在这里插入图片描述

完整项目的结构如下:
在这里插入图片描述

5、参考

1、华为官网:@ohos.security.asset (关键资产存储服务)

2、冉冉同学:【HarmonyOS NEXT】获取卸载APP后不变的设备ID

相关文章:

【鸿蒙NEXT】鸿蒙里面类似iOS的Keychain——关键资产(@ohos.security.asset)实现设备唯一标识

前言 在iOS开发中Keychain 是一个非常安全的存储系统&#xff0c;用于保存敏感信息&#xff0c;如密码、证书、密钥等。与 NSUserDefaults 或文件系统不同&#xff0c;Keychain 提供了更高的安全性&#xff0c;因为它对数据进行了加密&#xff0c;并且只有经过授权的应用程序才…...

学习笔记 --C#基础其他知识点(数据结构)

C#中的数据结构《二》–视频学习笔记 在数据结构的分类&#xff1a; 1.集合 2.线性 3.树形 4.图状结构 数据结构是数据在程序中的存储结构&#xff0c;和基本的数据操作 算法&#xff1a;解决问题的解决思路&#xff0c;基于数据结构 本课程包括&#xff1a;线性表&#xff…...

AI与药学 | ChatGPT 在临床药学中的有效性以及人工智能在药物治疗管理中的作用

《Effectiveness of ChatGPT in clinical pharmacy and the role of artificial intelligence in medication therapy management》这篇文献研究了ChatGPT在临床药学&#xff0c;特别是在药物治疗管理&#xff08;MTM&#xff09;中的有效性。 一、研究背景 (Background) MTM …...

Streamlining QA with Automated Testing for 3D Models

Quality assurance testing in 3D modeling is like walking a tightrope. Balancing the need for detailed accuracy and the time it takes to achieve it is no small feat. But what if we could make the tightrope wider, the task less daunting? And it’s where aut…...

产品原型设计

&#x1f923;&#x1f923;目录&#x1f923;&#x1f923; 一、Axure原型设计&#xff08;Axure RP 9 &#xff09;1.1 软件下载安装1.2 产品原型展示1.3 产品原型下载1.4 视频课程推荐 二、磨刀原型设计2.1 软件下载安装2.2 产品原型展示2.3 产品原型下载2.4 视频课程推荐 什…...

【Linux命令】su、sudo、sudo su、sudo -i、sudo -l的用法和区别

su 命令 su (Switch User 切换用户)&#xff0c;允许用户切换到另一个用户的身份&#xff0c;默认情况下是切换到 root 用户。 默认行为&#xff1a;如果只运行 su&#xff0c;则系统会要求输入 root 用户的密码来切换到 root 用户&#xff0c;获取管理员权限。 切换到其他用…...

【广州计算机学会、广州互联网协会联合主办 | ACM独立出版 | 高录用】第四届大数据、信息与计算机网络国际学术会议(BDICN 2025)

第四届大数据、信息与计算机网络国际学术会议&#xff08;BDICN 2025&#xff09;定于2025年01月10-12日在中国广州举行。会议旨在为从事“大数据”、“计算机网络”与“信息”研究的专家学者、工程技术人员、技术研发人员提供一个共享科研成果和前沿技术&#xff0c;了解学术发…...

HTML5 开关(Toggle Switch)详细讲解

HTML5 开关&#xff08;Toggle Switch&#xff09;详细讲解 1. 任务概述 开关&#xff08;Toggle Switch&#xff09;是一种用于表示二元状态&#xff08;如开/关&#xff09;的用户界面控件。用户可以通过点击开关来切换状态&#xff0c;常见于设置选项、开关功能等场景。 2…...

win32汇编环境下,双击窗口程序内生成的listview列表控件的某行,并提取其内容的示例程序

;运行效果 ;双击后 ;上源码&#xff0c;仔细研究里面的几条备注就理解原理了 ;提取窗口程序内生成的listview列表控件的内容示例程序 ;抄下面源码&#xff0c;可以在radasm里面直接编译运行。主要的部分加了备注。 ;>>>>>>>>>>>>>>…...

mysql主从断开后问题排查及修复

服务器mysql主从同步断开后&#xff0c;进行主从恢复 问题&#xff1a;mysql的主从断开了&#xff0c;在从服务器上通过以下命令查看状态&#xff0c;发现Slave_SQL_Running为No SHOW SLAVE STATUS\G;Slave_IO_Running&#xff1a;表示从服务器读取主服务器日志的线程 Slave_…...

[2025] 如何在 Windows 计算机上轻松越狱 IOS 设备

笔记 1. 首次启动越狱工具时&#xff0c;会提示您安装驱动程序。单击“是”确认安装&#xff0c;然后再次运行越狱工具。 2. 对于Apple 6s-7P和iPad系列&#xff08;iOS14.4及以上&#xff09;&#xff0c;您应该点击“Optinos”并勾选“允许未经测试的iOS/iPadOS/tvOS版本”&…...

计算机网络—————考研复试

第一章、计算机网络体系结构 1. OSI参考模型和TCP/IP模型&#xff1a; OSI与TCP/IP的记忆方法&#xff1a;只需把OSI的七层记住&#xff0c;将应用层、表示层、会话层一起记&#xff0c;到TCP/IP变成应用层。物理层和数据链路层换成网络接口层。把网络层换个字变成网际层。 而…...

[pdf、epub]260道《软件方法》强化自测题业务建模需求分析共216页(202412更新)

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 260道《软件方法》强化自测题业务建模需求分析共216页&#xff08;202412更新&#xff09; 已上传到本CSDN账号的资源 如果下载不到&#xff0c;也可以访问以下链接&#xff1a; ht…...

LeetCode - 初级算法 数组(只出现一次的数字)

只出现一次的数字 这篇文章讨论如何找到一个数组中只出现一次的数字,确保算法的时间复杂度为线性,且只使用常量额外空间。 免责声明:本文来源于个人知识与公开资料,仅用于学术交流。 描述 给定一个非空整数数组 nums,除了某个元素只出现一次以外,其余每个元素均出现两…...

Android性能优化概述

应用启动速度慢页面加载慢交互卡顿CrashANR 针对这些问题&#xff0c;可以逐一进行优化 1.启动优化 Android应用启动分为冷启动和热启动以及初次安装启动&#xff0c;此处只分析冷启动及热启动 冷启动&#xff1a;当设备无该应用进程时&#xff0c;用户操作拉起应用&#xff…...

C++ 实现map容器从大到小排序

map容器默认从小到大排序 利用仿函数可以修改map容器的排序规则为从大到小 示例&#xff1a; #include<iostream> #include<string> #include<map> using namespace std; class MyCompare { public: bool operator()(const int v1, const int v2) co…...

java中的文件操作

基础知识 1.File类对象的常用方法 一、 创建文件和目录 1.createNewFile() 用于创建一个新的文件&#xff0c;如果文件已经存在&#xff0c;则返回 false File file new File("C:\\Users\\P51\\Desktop\\file.txt"); file.createNewFile(); 2.mkdir() 用于创建一个…...

修复OpenHarmony系统相机应用横屏拍照按钮点不到的问题

适配OpenHarmony系统相机应用横屏UI&#xff0c; 相关pr: https://gitee.com/openharmony/applications_camera/pulls/233/files 适配效果 如何安装 编译好的hap提供在附件中 1.预置在源码&#xff0c;随固件安装 2.安装hap hdc shell "mount -o remount,rw /"…...

2024165读书笔记|《飞花令·合》——人生飘忽百年内,且须酣畅万古情

2024165读书笔记|《飞花令合》—— 人生飘忽百年内&#xff0c;且须酣畅万古情 屈原班婕妤曹植刘绘卢思道卢照邻苏味道刘希夷李白高适杜甫司空曙白居易温庭筠韦庄窦叔向张泌林逋柳永晏殊欧阳修李觏舒亶秦观陈瓘李清照陆游辛弃疾姜夔蒋捷吴伟业纳兰性德张惠言邓廷桢 《飞花令合》…...

哈夫曼编码(Huffman Coding)与哈夫曼树(Huffman Tree)

已知字符集{a,b,c,d,e,f}&#xff0c;若各字符出现的次数分别为6&#xff0c;3&#xff0c;8&#xff0c;2&#xff0c;10&#xff0c;4&#xff0c;则对应字符集中各字符的哈夫曼编码可能是&#xff08; &#xff09;。 A.00&#xff0c;1011&#xff0c;01&#xff0…...

Django项目中高效管理和使用选择常量

引言 在开发Django项目时,我们经常需要处理各种选择字段,比如用户类型、订单状态或产品分类等。如何有效地管理这些选择常量,使其在整个项目中保持一致性,同时又易于维护和更新呢?本文将介绍一种在Django项目中集中管理和使用选择常量的方法。 正文 © ivwdcwso (I…...

拦截器(Interceptor)的使用

在Java Web开发中&#xff0c;拦截器&#xff08;Interceptor&#xff09;是一种动态拦截请求和响应的对象&#xff0c;它可以在请求被控制器处理之前和之后执行一些预处理和后处理逻辑。要定义一个拦截器并使其生效&#xff0c;通常需要以下几个步骤&#xff1a; 1. 定义拦截…...

线段树例题题解

卫星覆盖&#xff08;NOI1997&#xff09; 题面&#xff1a; SERCOI&#xff08;Space-Earth Resource Cover-Observe lnstitute&#xff09; 是一个致力于利用卫星技术对空间和地球资源进行覆盖观测的组织。现在他们研制成功一种新型资源观测卫星 -SERCOI-308。这种卫星可以…...

AI提示词工程的“优化背后”:如何通过精准提示提升模型性能?

提示词工程(Prompt Engineering)已经成为推动AI模型如GPT等发挥其强大能力的核心。AI模型的输出质量与输入的提示词密切相关。因为之前已经大致用过一段时间提示词,所以这篇文章集中在有一定基础,起码对提示词不陌生,想要去设计和优化提示词+处理复杂问题的时候不知道如何…...

c# Record关键字

在 C# 9.0 中引入了 record 关键字&#xff0c;用于定义记录类型&#xff08;Record Types&#xff09;。记录类型是一种轻量级的数据载体&#xff0c;专注于表示数据&#xff0c;它提供了内置的相等性比较、生成属性和方法等功能&#xff0c;使得编写数据类更加简洁和高效。 …...

高效管理 Nginx 的利器:nginxWebUI 指南和 Docker 部署安装过程

前言 Nginx WebUI 是一个为 Nginx 提供图形化管理界面的工具。通过 WebUI&#xff0c;用户可以轻松管理 Nginx 配置&#xff0c;而无需直接编辑配置文件&#xff0c;尤其适合新手用户和频繁修改配置的场景。 官网文档&#xff1a;nginxWebUI - 文档 本文将分享为什么选择 ngin…...

家政预约小程序04活动管理表结构设计

目录 1 创建活动表2 创建活动规则表3 创建活动参与记录表总结 为了满足我们日常的营销&#xff0c;我们通常需要搞一些活动&#xff0c;比如满减、折扣、团购等。启动活动后&#xff0c;会在首页进行显示&#xff0c;当用户访问小程序的时候&#xff0c;就可以参与活动&#xf…...

谷歌浏览器的在线存储功能使用方法

谷歌浏览器不仅是目前全球使用最广泛的网络浏览器之一&#xff0c;它还集成了许多实用的功能来提升用户体验。其中&#xff0c;谷歌浏览器的在线存储功能允许用户将数据保存在云端&#xff0c;实现跨设备的无缝同步和共享。本文将详细介绍如何在谷歌浏览器中使用这一功能。 一、…...

HT-HaiBOX边缘计算盒 智慧工厂方案,智慧医疗方案,智慧加油站方案,智慧安防方案,智慧城市方案;方案定制开发

背景介绍 在当今数字化时代&#xff0c;各个行业对于智能化视频监控设备的需求日益增长。无论是安防监控&#xff0c;还是智慧工厂、智慧城市等领域&#xff0c;都需要高效、智能的设备来保障安全和提高生产效率。然而&#xff0c;传统的视频监控设备存在诸多痛点&#xff1a;…...

回调机制实现观察者模式

观察者设计模式&#xff0c;允许对象在状态变化时通知其他依赖对象&#xff0c;通常通过回调函数实现。 在回调机制中&#xff0c;可以注册多个回调函数&#xff0c;以便在特定事件发生时依次调用它们。下面是一个示例&#xff0c;展示如何在 C 中实现一个简单的事件管理器&am…...