【鸿蒙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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/65825.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用npm包的工程如何引入mapboxgl-enhance/maplibre-gl-enhance扩展包

作者&#xff1a;刘大 前言 在使用iClient for MapboxGL/MapLibreGL项目开发中&#xff0c;往往会对接非EPSG:3857坐标系的地图&#xff0c;由于默认不支持&#xff0c;因此需引入mapboxgl-enhance/maplibre-gl-enhance扩展包。 在使用Vue等其他框架&#xff0c;通过npm包下载…

应急指挥系统总体架构方案

引言 应急指挥系统总体架构方案旨在构建一个高效、智能的应急管理体系&#xff0c;以应对自然灾害、事故灾难等突发事件&#xff0c;保障人民生命财产安全。 背景与挑战 近年来&#xff0c;安全生产形势严峻&#xff0c;自然灾害事故频发&#xff0c;对应急指挥系统的要求越…

如何用CSS3创建圆角矩形并居中显示?

在网页设计中&#xff0c;圆角矩形因其美观和现代感而被广泛使用&#xff0c;居中显示元素也是一个常见的需求。今天&#xff0c;我们将学习如何使用CSS3的border-radius属性来创建圆角矩形&#xff0c;并将其居中显示在页面上。 如果你正在学习CSS&#xff0c;那么这个实例将非…

UE5通过蓝图节点控制材质参数

通过蓝图节点控制材质的参数 蓝图节点 在材质上设置标量值 和 在材质上设置向量参数值 Set Scalar Parameter Value on Materials Set Vector Parameter Value on Materials 这两个蓝图节点都可以在蓝图中&#xff0c;控制材质的参数值和向量值

canvas+fabric实现时间刻度尺(二)

前言 我们前面实现了时间刻度尺&#xff0c;鼠标移动显示时间&#xff0c;接下来我们实现鼠标点击某个时间进行弹框。 效果 实现 1.监听鼠标按下事件 2.编写弹框页面 3.时间转换 <template><div><canvas id"rulerCanvas" width"1200"…

手机实时提取SIM卡打电话的信令声音-双卡手机来电如何获取哪一个卡的来电

手机实时提取SIM卡打电话的信令声音 --双卡手机来电如何获取哪一个卡的来电 一、前言 前面的篇章《手机实时提取SIM卡打电话的信令声音-智能拨号器的双SIM卡切换方案》中&#xff0c;我们论述了局域网SIP坐席通过手机外呼出去时&#xff0c;手机中主副卡的呼叫调度策略。 但…

离线语音识别+青云客语音机器人(幼儿园级别教程)

1、使用步骤 确保已安装以下库&#xff1a; pip install vosk sounddevice requests pyttsx3 2、下载 Vosk 模型&#xff1a; 下载适合的中文模型&#xff0c;如 vosk-model-small-cn-0.22。 下载地址&#xff1a; https://alphacephei.com/vosk/models 将模型解压后放置在…

Streaming Dense Video Captioning

原文出处 CVPR 原文链接 [2404.01297] Streaming Dense Video Captioninghttps://arxiv.org/abs/2404.01297 原文笔记 What 1、提出了一种基于聚类传入token的新记忆模块&#xff0c;该模块可以处理任意长的视频&#xff0c;并且可以在不访问视频所有帧的情况下处理视频(…

TCP 连接:三次握手与四次挥手

TCP 协议&#xff0c;全称为“传输控制协议”。 1. TCP 协议段格式 给出几个定义 &#xff1a; 16位源端口号 &#xff1a;用于标识发送端的应用程序。 16位目的端口号 &#xff1a;用于标识接收端的目标应用程序。 32位序号 &#xff1a;用于标识发送的每一个字节流中的第一…

IDEA+Docker一键部署项目SpringBoot项目

文章目录 1. 部署项目的传统方式2. 前置工作3. SSH配置4. 连接Docker守护进程5. 创建简单的SpringBoot应用程序6. 编写Dockerfile文件7. 配置远程部署 7.1 创建配置7.2 绑定端口7.3 添加执行前要运行的任务 8. 部署项目9. 开放防火墙的 11020 端口10. 访问项目11. 可能遇到的问…

redis开发与运维-redis0401-补充-redis流水线与Jedis执行流水线

文章目录 【README】【1】redis流水线Pipeline【1.1】redis流水线概念【1.2】redis流水线性能测试【1.2.1】使用流水线与未使用流水线的性能对比【1.2.2】使用流水线与redis原生批量命令的性能对比【1.2.3】流水线缺点 【1.3】Jedis客户端执行流水线【1.3.1】Jedis客户端执行流…

Uncaught ReferenceError: __VUE_HMR_RUNTIME__ is not defined

Syntax Error: Error: vitejs/plugin-vue requires vue (>3.2.13) or vue/compiler-sfc to be present in the dependency tree. 第一步 npm install vue/compiler-sfc npm run dev 运行成功&#xff0c;本地打开页面是空白&#xff0c;控制台报错 重新下载了vue-loa…

【微服务】【Sentinel】认识Sentinel

文章目录 1. 雪崩问题2. 解决方案3. 服务保护技术对比4. 安装 Sentinel4.1 启动控制台4.2 客户端接入控制台 参考资料: 1. 雪崩问题 微服务调用链路中的某个服务故障&#xff0c;引起整个链路中的所有微服务都不可用&#xff0c;这就是雪崩。动图演示&#xff1a; 在微服务系统…

STM32完全学习——使用定时器1精确延时

一、定时器的相关配置 首先一定要是递减定时器&#xff0c;递增的不太行&#xff0c;控制的不够准确&#xff0c;其次在大于10微秒的延时是非常准确的&#xff0c;小于的话&#xff0c;就没有那没准&#xff0c;但是凑合能用。误差都在一个微秒以内。使用高级定时器也就是时钟…

connect to host github.com port 22: Connection timed out 的解决方法

原因是 Github 被 GFW 屏蔽了。 Windows 系统&#xff0c;打开 C:\Windows\System32\drivers\etc&#xff0c;复制其中的 hosts 文件至桌面&#xff0c;用文本编辑器或者其他工具打开。 复制以下内容进去&#xff1a; 140.82.114.4 github.com 151.101.1.6 github.global.ss…

常见的排序算法过程和比较分析

比较分析 排序类别排序算法时间复杂度&#xff08;最好&#xff09;时间复杂度&#xff08;最坏&#xff09;时间复杂度&#xff08;平均&#xff09;辅助空间复杂度稳定性插入排序直接插入排序O(n)O(n)O(n)O(1)稳定插入排序折半插入排序O(n)O(n)O(n)O(1)稳定插入排序希尔排序…

【QGIS入门实战精品教程】7.3:QGIS制作千层饼(DEM+等高线+影像+TIN)

文章目录 一、效果展示二、数据准备三、制作过程1. 打开软件2. 添加图层3. 制作千层饼一、效果展示 二、数据准备 订阅专栏后,从专栏配套案例数据包中的7.3.rar中获取。 1. dem 2. 影像 3. 等高线 4. tin 三、制作过程 1. 打开软件 打开QGIS软件。 QGIS软件主界面。

如何将CSDN文章 导出为 PDF文件

一、首先&#xff0c;打开我们想要导出为 PDF格式的 CSDN文章&#xff0c;以下图为例。 二、按 F12 调出浏览器调式模式后&#xff0c;选择 控制台 三、在控制台处粘贴代码 代码&#xff1a; (function(){ use strict;var articleBox $("div.article_content"…

HTML——54. form元素属性

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>form元素属性</title></head><body><!--form标签用于创建一个表单&#xff0c;会将里面的内容一起发送服务器&#xff0c;结构类似于表格--><!-…

告别 $arr[0]: PHP 和 Laravel 中更优雅的数组处理方式

你是否曾经历过这样的惊魂时刻&#xff1a;线上代码突然崩溃&#xff0c;只因访问了一个不存在的数组元素&#xff1f;或者更糟的是&#xff0c;应用开始疯狂抛出错误&#xff0c;而你却毫无头绪&#xff1f;这一切的罪魁祸首可能就是看似人畜无害的硬编码数组索引&#xff0c;…