HarmonyOs应用权限申请,system_grant和user_grant区别。本文附头像上传申请user-grant权限代码示例

news/2024/9/18 2:52:39/ 标签: harmonyos, 鸿蒙, 华为

HarmonyOs应用权限申请,system_grant和user_grant区别。本文附头像上传申请user-grant权限代码示例

system_grant(系统授权)

system_grant指的是系统授权类型,在该类型的权限许可下,应用被允许访问的数据不会涉及到用户或设备的敏感信息,应用被允许执行的操作对系统或者其他应用产生的影响可控

user_grant(用户授权)

user_grant指的是用户授权类型,在该类型的权限许可下,应用被允许访问的数据将会涉及到用户或设备的敏感信息,应用被允许执行的操作可能对系统或者其他应用产生严重的影响。

权限APL等级

根据权限对于不同等级应用有不同的开放范围,权限类型对应分为以下三个等级,等级依次提高。

APL级别说明开放范围
normal允许应用访问超出默认规则外的普通系统资源,如配置Wi-Fi信息、调用相机拍摄等。这些系统资源的开放(包括数据和功能)对用户隐私以及其他应用带来的风险低。APL等级为normal及以上的应用。
system_basic允许应用访问操作系统基础服务(系统提供或者预置的基础功能)相关的资源,如系统设置、身份认证等。这些系统资源的开放对用户隐私以及其他应用带来的风险较高。- APL等级为system_basic及以上的应用。- 部分权限对normal级别的应用受限开放,这部分权限在本指导中描述为“受限开放权限”。
system_core涉及开放操作系统核心资源的访问操作。这部分系统资源是系统最核心的底层服务,如果遭受破坏,操作系统将无法正常运行。- APL等级为system_core的应用。- 仅对系统应用开放。
  • 每次执行需要目标权限的操作时,应用都必须检查自己是否已经具有该权限。

    如需检查用户是否已向您的应用授予特定权限,可以使用checkAccessToken()函数,此方法会返回Promise<[GrantStatus]>:PERMISSION_GRANTED或PERMISSION_DENIED。

  • 每次访问受目标权限保护的接口之前,都需要使用requestPermissionsFromUser()接口请求相应的权限。

    用户可能在动态授予权限后通过系统设置来取消应用的权限,因此不能将之前授予的授权状态持久化。

  • user_grant权限授权要基于用户可知可控的原则,需要应用在运行时主动调用系统动态申请权限的接口,系统弹框由用户授权,用户结合应用运行场景的上下文,识别出应用申请相应敏感权限的合理性,从而做出正确的选择。

  • 系统不鼓励频繁弹窗打扰用户,如果用户拒绝授权,将无法再次拉起弹窗,需要应用引导用户在系统应用“设置”的界面中手动授予权限。

  • API12新增可以二次向用户申请授权requestPermissionOnSetting()

  • 系统权限弹窗不可被遮挡。

    系统权限弹窗不可被其他组件/控件遮挡,弹窗信息需要完整展示,以便用户识别并完成授权动作。

    如果系统权限弹窗与其他组件/控件同时同位置展示,系统权限弹窗将默认覆盖其他组件/控件。

代码示例:点击头像申请访问相机和相册媒体的权限,拒绝后二次点击半模态弹窗按钮向用户进行二次权限申请:
实现思路:
1.点击头像时利用checkAccessToken()检查当前是否已拥有权限,如果有则直接使用,如果没有则使用requestPermissionsFromUser()进行向用户询问权限授权
2.如果用户全部拒绝,下一次使用时则使用requestPermissionOnSetting()进行二次用户询问拉起弹窗(也可以引导用户去设置界面手动授权,在requestPermissionsFromUser()拒绝策略中即可实现)

//校验权限工具类

import { abilityAccessCtrl,Context, bundleManager, common, Permissions } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
/*** 校验当前是否已经授权(已封装成工具类,如使用三层架构则可放入common中)* @param permission* @returns*/
export async function checkPermissionGrant(permission: Permissions): Promise<abilityAccessCtrl.GrantStatus> {let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();let grantStatus: abilityAccessCtrl.GrantStatus = abilityAccessCtrl.GrantStatus.PERMISSION_DENIED;// 获取应用程序的accessTokenIDlet tokenId: number = 0;try {let bundleInfo: bundleManager.BundleInfo = await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);let appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo;tokenId = appInfo.accessTokenId;} catch (error) {const err: BusinessError = error as BusinessError;console.error(`Failed to get bundle info for self. Code is ${err.code}, message is ${err.message}`);}// 校验应用是否被授予权限try {grantStatus = await atManager.checkAccessToken(tokenId, permission);} catch (error) {const err: BusinessError = error as BusinessError;console.error(`Failed to check access token. Code is ${err.code}, message is ${err.message}`);}return grantStatus;
}
/***向用户二次询问拉起弹窗授权*/
export function reqPermissionTwice(permissions: Array<Permissions>, context: common.UIAbilityContext): Promise<Array<abilityAccessCtrl.GrantStatus>> {let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();return atManager.requestPermissionOnSetting(context, permissions)
}

//主页面(其中涉及一些工具类未提供,不影响本次示例功能实现,布局会有影响。项目代码还在开发中,之后会开源)

import {AppStorageEnum,NavigationBar,HdUser,hdHttp,cameraCapture,HdLoadingDialo,logger,authStore,reqPermissionTwice,checkPermissionGrant} from "@ss/basic"
import { fileIo, picker } from '@kit.CoreFileKit'
import { BusinessError, request } from '@kit.BasicServicesKit'
import { promptAction } from '@kit.ArkUI'
import { router } from '@kit.ArkUI'
import { abilityAccessCtrl, common, Permissions } from '@kit.AbilityKit'@Component
export struct MineIndexView {pageInfos: NavPathStack = new NavPathStack()@StorageProp(AppStorageEnum.TOP_AVOID_HEIGHT)avoidTopHeight: number = 0@StorageProp(AppStorageEnum.LOGIN_USER)loginUser: HdUser = {} as HdUser@State isShowSheet: boolean = falsedialog: CustomDialogController = new CustomDialogController({builder: HdLoadingDialog({ message: '更新中...' }),customStyle: true,alignment: DialogAlignment.Center})permissions: Array<Permissions> = ['ohos.permission.CAMERA', 'ohos.permission.WRITE_MEDIA'];isGrantFirst: boolean = falseaboutToAppear(): void {console.log(JSON.stringify(this.loginUser))// this.checkPermissions()}async checkPermissions(permission: Permissions): Promise<void> {//通过checkAccessToken校验本次usergrant是否已授权let grantStatus: abilityAccessCtrl.GrantStatus = await checkPermissionGrant(permission);//根据返回结果处理业务const currentPermission: Array<Permissions> = [permission]if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) { //授权logger.info("已授权")if (currentPermission[0] == this.permissions[0]) {this.updateAvatarForCamera()} else {this.updateAvatarForPhotos()}} else { //未授权logger.info("未授权")if (this.isGrantFirst) { //因为二次询问授权之前必须调用一次requestPermissionsFromUser//二次向用户申请授权api12拥有reqPermissionTwice(currentPermission, getContext(this) as common.UIAbilityContext).then((data: Array<abilityAccessCtrl.GrantStatus>) => {if (data[0] == abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {if (currentPermission[0] == this.permissions[0]) {this.updateAvatarForCamera()} else {this.updateAvatarForPhotos()}}}).catch((err: BusinessError) => {console.error('data:' + JSON.stringify(err));});}}}//询问用户是否授权,只会调起一次弹窗reqPermissionsFromUser(permissions: Array<Permissions>, context: common.UIAbilityContext): void {let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();// requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗atManager.requestPermissionsFromUser(context, permissions).then((data) => {let grantStatus: Array<number> = data.authResults;let length: number = grantStatus.length;let count: number = 0for (let i = 0; i < length; i++) {count = count + 1if (grantStatus[i] === 0) {// 用户授权,可以继续访问目标操作this.isShowSheet = true} else {// 用户拒绝授权,提示用户必须授权才能访问当前页面的功能,并引导用户到系统设置中打开相应的权限if(count == length)this.isShowSheet = false}}// 授权成功}).catch((err: BusinessError) => {console.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`);})}/*** 唤起相机上传图片*/async updateAvatarForCamera() {//调用相机唤起工具类let URI = await cameraCapture(getContext(this) as common.UIAbilityContext)if (URI != undefined) {// 2. 存储到沙箱中(fileIo在API9之后就废弃了,使用fs替换)const context = getContext(this)const fileType = 'jpg'const fileName = Date.now() + '.' + fileTypeconst copyFilePath = context.cacheDir + '/' + fileNameconst file = fileIo.openSync(URI, fileIo.OpenMode.READ_ONLY)fileIo.copyFileSync(file.fd, copyFilePath)// 3. 准备请求配置const config: request.UploadConfig = {url: hdHttp.baseURL + 'userInfo/avatar',method: 'POST',header: {'Accept': '*/*','Authorization': `Bearer ${this.loginUser.token}`,'Content-Type': 'multipart/form-data'},files: [{name: 'file', //multipart提交时,表单项目的名称,缺省为fileuri: `internal://cache/` + fileName, //仅支持"internal"协议类型,"internal://cache/"为应用的私有目录,是必填字段type: fileType, //文件的内容类型,默认根据文件名或路径的后缀获取。filename: fileName //multipart提交时,请求头中的文件名。}],data: []}// 4. 开始上传this.dialog.open() //自定义加载弹窗request.uploadFile(context, config, (err, data) => {if (err) {return logger.error('UPLOAD', err.message)}data.on('complete', () => {// 5. 更新头像hdHttp.get<HdUser>('userInfo').then(res => {this.loginUser.avatar = res.data.avatarauthStore.setUser(this.loginUser)promptAction.showToast({ message: '更新头像成功' }) //提示弹窗this.dialog.close()})})})}}/*** 唤起相册上传图片*/updateAvatarForPhotos() {const photoSelectOptions = new picker.PhotoSelectOptions()photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPEphotoSelectOptions.maxSelectNumber = 1const photoViewPicker = new picker.PhotoViewPicker()photoViewPicker.select(photoSelectOptions).then(result => {// 1. 得到文件路径const URI = result.photoUris[0]// 2. 存储到沙箱中(fileIo在API9之后就废弃了,使用fs替换)const context = getContext(this)const fileType = 'jpg'const fileName = Date.now() + '.' + fileTypeconst copyFilePath = context.cacheDir + '/' + fileNameconst file = fileIo.openSync(URI, fileIo.OpenMode.READ_ONLY)fileIo.copyFileSync(file.fd, copyFilePath)// 3. 准备请求配置const config: request.UploadConfig = {url: hdHttp.baseURL + 'userInfo/avatar',method: 'POST',header: {'Accept': '*/*','Authorization': `Bearer ${this.loginUser.token}`,'Content-Type': 'multipart/form-data'},files: [{name: 'file', //multipart提交时,表单项目的名称,缺省为fileuri: `internal://cache/` + fileName, //仅支持"internal"协议类型,"internal://cache/"为应用的私有目录,是必填字段type: fileType, //文件的内容类型,默认根据文件名或路径的后缀获取。filename: fileName //multipart提交时,请求头中的文件名。}],data: []}// 4. 开始上传this.dialog.open() //自定义加载弹窗request.uploadFile(context, config, (err, data) => {if (err) {return logger.error('UPLOAD', err.message)}data.on('complete', () => {// 5. 更新头像hdHttp.get<HdUser>('userInfo').then(res => {this.loginUser.avatar = res.data.avatarauthStore.setUser(this.loginUser)promptAction.showToast({ message: '更新头像成功' }) //提示弹窗this.dialog.close()})})})})}@BuilderbuilderMenuBack() {Button({ type: ButtonType.Circle }) { //设置按钮的类型未圆形按钮Image($r("app.media.left"))}.onClick(() => {router.back()}).width(25).height(25).backgroundColor('rgba(255, 255, 255, 0)')}@BuilderbuilderMenuMail() {Button({ type: ButtonType.Circle }) { //设置按钮的类型未圆形按钮Image($r("app.media.mail"))}.onClick(() => {this.pageInfos.pushPathByName("MailCenterView", false);}).width(25).height(25).backgroundColor('rgba(255, 255, 255, 0)')}/*** 半模态对话框*/@BuilderbuildAvatarSheet() {Column() {Text("拍照").width('100%').height('50%').lineHeight('45%').textAlign(TextAlign.Center).backgroundColor('#e5e7ed').onClick(() => {this.isShowSheet = !this.isShowSheetthis.checkPermissions(this.permissions[0])})Divider().color(Color.White)Text("从相册选择").width('100%').height('50%').lineHeight('45%').textAlign(TextAlign.Center).backgroundColor('#e5e7ed').onClick(() => {//并非多次一举,再调用reqPermissionsFromUser之前如果打开半模态化转场会阻塞主线程(原因不知)if(!this.isGrantFirst) {this.isGrantFirst = truethis.reqPermissionsFromUser(this.permissions, getContext(this) as common.UIAbilityContext)}else{this.isShowSheet = !this.isShowSheet}})}.width('100%').height(150)}build() {Navigation(this.pageInfos) {Column() {NavigationBar({buildButtonBackParam: () => {this.builderMenuBack()},buildMenuOneParam: () => {this.builderMenuMail()}})Flex({ alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center, direction: FlexDirection.Column }) {Image($rawfile('kyrie.jpg')).id("mine").width(90).height(90).clip(true).borderRadius(45).margin({ top: 45 }).geometryTransition("avatar").transition(TransitionEffect.opacity(0.99)).onClick(() => {this.isShowSheet = !this.isShowSheetif(!this.isGrantFirst)this.reqPermissionsFromUser(this.permissions, getContext(this) as common.UIAbilityContext)})Text(this.loginUser.username).fontWeight(700).fontSize(16).margin({ top: 8 })Button({ type: ButtonType.Capsule, stateEffect: false }) {Text("终身大会员").fontColor("#7f8085").fontSize(12)}.width(150).height(32).margin({ top: 8 }).backgroundColor("#dbdce1")}.bindSheet(this.isShowSheet, this.buildAvatarSheet(), {height: 150,dragBar: false,showClose: false,onDisappear: () => {this.isShowSheet = false}}).width("90%").height(200)}.width('100%').transition(TransitionEffect.asymmetric(TransitionEffect.opacity(0.99),TransitionEffect.OPACITY)).height('100%')}.backgroundColor("#e4e5ea")}
}

效果图:
1.第一次点击头像
在这里插入图片描述
2.假如全部拒绝,第二次访问继续询问(具体业务权限)
在这里插入图片描述


http://www.ppmy.cn/news/1515881.html

相关文章

状态压缩DP---最短Hamilton路径

给定一张 nn 个点的带权无向图&#xff0c;点从 0∼n−10∼n−1 标号&#xff0c;求起点 00 到终点 n−1n−1 的最短 Hamilton 路径。 Hamilton 路径的定义是从 00 到 n−1n−1 不重不漏地经过每个点恰好一次。 输入格式 第一行输入整数 nn。 接下来 nn 行每行 nn 个整数&a…

火绒使用详解 为什么选择火绒?使用了自定义规则及其高级功能的火绒,为什么能吊打卡巴斯基,360,瑞星,惠普联想戴尔的电脑管家等?

目录 前言 必看 为什么选择火绒&#xff1f; 使用了自定义规则及其高级功能的火绒&#xff0c;为什么能吊打卡巴斯基&#xff0c;360&#xff0c;瑞星&#xff0c;惠普联想戴尔的电脑管家等&#xff1f; 原因如下&#xff1a; 火绒的主要优势 1. 轻量化设计 2. 强大的自…

wsl2 airsim wairing for connect (Windows11 UE4.27)问题解决

一、概述 这里记述我遇到我在使用wsl2子系统与Windows11上进行交互时候&#xff0c;遇到的一些我之前没有遇到过的问题。 之前的我写的配置链接在这里。 UE5 with plugins AirSim in Windows & ROS in WSL2-Ubuntu 20.04配置过程记录_airsim ue5-CSDN博客文章浏览阅读455次…

python循环访问excel的某一列从某行开始的内容

python循环访问excel的某一列从某一行开始的内容 您可以使用openpyxl库来实现这个需求。以下是一个示例代码&#xff0c;展示了如何使用openpyxl读取Excel文件中的特定列&#xff08;假设为第一列&#xff0c;从第二行开始&#xff09;&#xff1a; 可以使用Python的openpyxl…

FastAPI+Vue3零基础开发ERP系统项目实战课 20240824上课笔记 循环和函数以及大量的练习

巩固一下 假设我们现在想要根据输入月份&#xff0c;判断是哪个季节&#xff0c;怎么做呀&#xff1f; 输入1&#xff1a;一月&#xff0c;12月&#xff0c;1月&#xff0c;2月是冬季 输入8&#xff1a;八月&#xff0c;夏季 作为思考题&#xff0c;有时间就做一下&#xff…

【HTML】为网页添加列表和超链接

1、列表 1.1、无序列表 <ul><li>列表项1</li><li>列表项2</li><li>列表项3</li> ...... </ul>无序列表的常用type属性值 1.2、有序列表 <ol><li>列表项1</li><li>列表项2</li><li>列…

GGD光伏电站低压并网柜,分布式光伏并网配电柜

作为光伏低压并网柜的厂家之一&#xff0c;江苏中盟电气集团以下为您分享关于分布式光伏发电中用到的低压并网柜&#xff08;并网配电柜&#xff09;的内容。 实际上GGD光伏低压并网柜&#xff0c;只是众多型号并网柜中的一种&#xff0c;它们以低压交流配电柜为基础&#xff…

Linux-远程访问及控制

系列文章目录 提示&#xff1a;仅用于个人学习&#xff0c;进行查漏补缺使用。 1.Linux-网络设置 2.Linux-DHCP服务、vsftp 3.Linux-DNS域名解析服务 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 系列文章目录…

排查Maven问题的步骤

0.检查pom文件完整性 1.检查IDEA中配置是否正确 2.使用清楚工具将所有的lastupdate清除, 3.有些依赖是公司的依赖 —>配置私服 —>拷贝同事仓库,覆盖自己的仓库 4.有了私服地址,但是还是下载不到 —>查看地址是否能访问 —>挂VPN

ISP 3A 算法:自动曝光(AE)中的平均亮度法详解

在自动曝光&#xff08;AE&#xff09;算法中&#xff0c;平均亮度法是一种经典且广泛应用的技术。它通过计算场景中所有像素的平均亮度来确定最佳曝光设置&#xff0c;从而保证图像的整体亮度处于适当的水平。尽管该方法相对简单&#xff0c;但它在AE算法中扮演着重要的角色&a…

React+Vis.js(07):vis.js设置关系网络图禁止缩放和设置关系的指向和名称

文章目录 设置关系图禁止缩放设置关系图的关系指向和关系名称设置关系图的关系指向:方式一设置关系图的关系指向:方式二设置关系显示的标签设置关系图禁止缩放 在 vis.js 中,你可以通过设置 interaction 配置项中的 zoomView 属性为 false 来禁止缩放功能。这样,用户将无法…

SQL Server数据库 创建表,和表的增删改查

打开SQL Server工具,连接服务器 右击数据库&#xff0c;创建新的数据库 新建表 填写列&#xff0c;我添加了Id,Name,Sex,Age,和class列 右键表刷新一下就有了 我又同时创建了一个Class表 点击新建查询&#xff0c;现在写代码添加数据&#xff0c;也可以操作表来对数据进行添加 …

Vim youcompleteme Windows 安装保姆级教程

不说废话。 准备 检查 Vim 的 Python 配置 安装好 vim 和 python 后&#xff08;python 必须 ≥ \ge ≥ 3.6&#xff09;&#xff0c;在 cmd 下运行 vim --version会弹出以下窗口。 如果发现 python/dyn 和 python3/dyn 都是 - &#xff08;我不知道只有前者是 能不能运行…

日志审计-graylog ssh登录超过6次告警

Apt 设备通过UDP收集日志&#xff0c;在gray创建接收端口192.168.0.187:1514 1、ssh登录失败次数大于5次 ssh日志级别默认为INFO级别&#xff0c;通过系统rsyslog模块处理&#xff0c;日志默认存储在/var/log/auth.log。 将日志转发到graylog vim /etc/rsyslog.conf 文件末…

第133天:内网安全-横向移动域控提权NetLogonADCSPACKDC永恒之蓝

案例一&#xff1a;横向移动-系统漏洞-CVE-2017-0146 这个漏洞就是大家熟悉的ms17-010&#xff0c;这里主要学习cs发送到msf&#xff0c;并且msf正向连接后续 原因是cs只能支持漏洞检测&#xff0c;而msf上有很多exp可以利用 注意msf不能使用4.5版本的有bug 这里还是反弹权…

2 种方式申请免费 SSL 证书,阿里云 Certbot

如何使用免费的 SSL 证书&#xff0c;有时在项目中需要使用免费的 SSL 证书&#xff0c;Aliyun 提供免费证书&#xff0c;三个月有效期&#xff0c;可以直接在aliyun 申请&#xff0c;搜索 SSL 证书&#xff0c;选择测试证书。 Aliyun 证书需要每三月来来换一次&#xff0c;页…

揭开CSS遮罩术:mask与-webkit-mask属性深度解析

标题&#xff1a;揭开CSS遮罩术&#xff1a;mask与-webkit-mask属性深度解析 摘要 CSS的mask属性是一种强大的工具&#xff0c;它允许开发者将图像或渐变用作遮罩层&#xff0c;以隐藏或显示元素的特定部分。-webkit-mask属性是其对应的Webkit前缀版本&#xff0c;用于在基于…

神经网络架构KAN确实具有一些独特的特点及底层原理和应用场景

Kolmogorov-Arnold Networks (KAN) 是基于 Kolmogorov-Arnold 表示定理的一类神经网络架构。其底层原理包括以下几个方面&#xff1a; 底层原理 Kolmogorov-Arnold 表示定理&#xff1a; 定理表明&#xff0c;任何连续函数都可以表示为一组单变量函数的有限叠加。这为多维函数…

Java笔试面试题AI答之线程(22)

文章目录 127. 简述Java 中的同步集合与并发集合有什么区别 &#xff1f;同步集合并发集合总结 128. 简述怎么检测一个线程是否拥有锁&#xff1f;1. 查阅文档和API2. 调试和日志3. 使用线程分析工具4. 自定义锁实现5. 锁监视器注意事项 129. 简述你如何在 Java 中获取线程堆栈…

一文总结Git的常用命令

基本概念 Git是一个分布式版本控制系统&#xff0c;用于管理和跟踪文件的变化。它可以追踪文件的每个版本&#xff0c;记录文件的修改历史&#xff0c;并允许用户在不同版本之间进行切换和合并。Git可以让多个开发人员同时对同一个代码库进行工作&#xff0c;而不会发生冲突。 …