HarmonyOS权限管理应用
权限管理
1. 权限管理概述
系统有一些比较敏感的数据和功能(通讯录、麦克风等),不能让应用软件随便访问,所以华为提供了一套授权管理机制来保护系统数据和功能,避免它们被不当或者恶意使用。
应用权限保护的对象可以分为数据和功能:
● 数据包括个人数据(如照片、通讯录、日历、位置等)、设备数据(如设备标识、相机、麦克风等)。
● 功能包括设备功能(如访问摄像头/麦克风、打电话、联网等)、应用功能(如弹出悬浮窗、创建快捷方式等)。
2. 授权方式
根据授权方式的不同,权限类型可分为system_grant(系统授权)和user_grant(用户授权)。
2.1 system_grant(系统授权)
🌶 system_grant指的是系统授权类型,在该类型的权限许可下,应用被允许访问的数据不会涉及到用户或设备的敏感信息,应用被允许执行的操作对系统或者其他应用产生的影响可控。
应用中申请了system_grant权限,系统会在用户安装应用时自动把相应权限授予给用户。
下面是一些常用的系统授权,比如:网络,蓝牙等
ohos.permission.INTERNET
允许使用Internet网络。
权限级别:normal
授权方式:system_grant
起始版本:9
2.2 user_grant(用户授权)
user_grant指的是用户授权类型,在该类型的权限许可下,应用被允许访问的数据将会涉及到用户或设备的敏感信息,应用被允许执行的操作可能对系统或者其他应用产生严重的影响。
user_grant类型权限不仅需要在安装包中申请权限,还需要在应用动态运行时,通过发送弹窗的方式请求用户授权。
在用户手动允许授权后,应用才会真正获取相应权限,从而成功访问操作目标对象。
ohos.permission.MICROPHONE
允许应用使用麦克风。
权限级别:normal
授权方式:user_grant
起始版本:8
3. 申请网络权限
网络权限是属于系统权限,是最基本的功能,申请权限也比较简单。应用需要在module.json5配置文件的
requestPermissions
标签中声明权限
3.1 配置网络权限
{"module": {"requestPermissions": [{"name": "ohos.permission.INTERNET"}]}
}
3.2 使用网络
Image("https://res2.vmallres.com/pimages/uomcdn/CN/pms/202403/gbom/6942103109577/428_428_326614F60FB96289584CA7B9E4AE7CB9mp.png").width(200).height(200)
4. 申请麦克风权限
4.1 配置ohos.permission.MICROPHONE权限
应用需要在module.json5的配置文件中逐个声明需要的权限,否则应用将无法获取授权
{"module" : {// ..."requestPermissions": [{"name": "ohos.permission.MICROPHONE", //权限名称(这里是麦克风权限)"reason": "$string:MICROPHONE_REASON", //原因"usedScene": { //该权限由谁发起"abilities": ["EntryAbility"], //由EntryAbility发起"when": "always" //发起时机(inuse使用时允许、always总是允许)}}],}
}
4.2 动态申请麦克风授权
动态申请授权指的是拉起系统授权弹窗,让用户选择“允许”或“禁止”,只有用户选择“允许”的情况下,才可以执行目标操作。我们以获取麦克风权限为例,让用户选择是否允许授权
let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager()
atManager.requestPermissionsFromUser(getContext(this),['ohos.permission.MICROPHONE']).then((data) => {let grantStatus: Array<number> = data.authResultslet length: number = grantStatus.length;for (let i = 0; i < length; i++) {if (grantStatus[i] === 0) {// 用户授权成功,可以继续访问目标操作console.info("授权成功,可以继续访问目标操作")} else {// 用户授权失败,不可以执行目标操作console.info("授权失败,不可以执行目标操作")}}
}).catch((err: BusinessError) => {console.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`);
});
4.3 校验当前应用是否已经授权
因为无法保证用户是否“允许”授权,所以每次在执行目标操作前,都需要进行检查是否有权限。可以通过调用checkAccessToken()方法来校验当前是否已经授权,如果已经授权,则可以直接访问目标操作;如果没有授权则提醒用户到系统设置手动打开应用权限。
//1.1 获取控制管理器
let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager()//1.2 获取应用程序的tokenId,它是应用程序的身份标识
let tokenId: number = 0;
try {let bundleInfo: bundleManager.BundleInfo =bundleManager.getBundleInfoForSelfSync(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}`);
}//1.3 校验应用是否被授予权限
let grantStatus: abilityAccessCtrl.GrantStatus = abilityAccessCtrl.GrantStatus.PERMISSION_DENIED
try {grantStatus = atManager.checkAccessTokenSync(tokenId, 'ohos.permission.MICROPHONE');
} catch (error) {const err: BusinessError = error as BusinessError;console.error(`Failed to check access token. Code is ${err.code}, message is ${err.message}`);
}//1.4 根据授权状态,决定是否执行目标操作
if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {//TODO 授权成功,执行已经授权的业务代码console.info("已经有授权,可以执行目标操作")
} else if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_DENIED) {//TODO 没有授权,引导用户去设置界面打开麦克风权限console.info("没有授权,不能执行目标操作")
}
下面是打开系统设置的代码
gotoSystemSetting() {const buildInfo =bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION)const context = getContext() as common.UIAbilityContextcontext.startAbility({bundleName: 'com.huawei.hmos.settings',abilityName: 'com.huawei.hmos.settings.MainAbility',uri: "application_info_entry",parameters: {pushParams: buildInfo.name}})
}
5. 封装权限工具类
5.1 权限工具类
import bundleManager from '@ohos.bundle.bundleManager';
import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';
import { BusinessError, Callback } from '@kit.BasicServicesKit';
import { common } from '@kit.AbilityKit';export class PermissionsUtils {/*** 校验应用是否具备制定的权限许可,返回权限许可状态* @param permission 指定权限* @returns 返回许可状态*/public static checkPermissionGrant(permission: Permissions, callback: Callbacks) {//1.1 获取控制管理器let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager()//1.2 获取应用程序的tokenId,它是应用程序的身份标识let tokenId: number = 0;try {let bundleInfo: bundleManager.BundleInfo =bundleManager.getBundleInfoForSelfSync(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}`);}//1.3 校验应用是否被授予权限let grantStatus: abilityAccessCtrl.GrantStatus = abilityAccessCtrl.GrantStatus.PERMISSION_DENIEDtry {grantStatus = atManager.checkAccessTokenSync(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}`);}//1.4 根据授权状态if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {//授权成功,执行已经授权的业务代码callback.onSuccess && callback.onSuccess()} else if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_DENIED) {if (callback.onFailed) {callback.onFailed()} else {//没有授权,引导用户去设置界面打开麦克风权限PermissionsUtils.gotoSystemSetting();}}return grantStatus}//2.动态请求用户授权public static requestPermissionsFromUser(permissions: Array<Permissions>,callback: Callbacks) {let context = getContext() as common.UIAbilityContextlet atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();atManager.requestPermissionsFromUser(context, permissions).then((data) => {let grantStatus: Array<number> = data.authResults;let length: number = grantStatus.length;for (let i = 0; i < length; i++) {if (grantStatus[i] === 0) {// 用户授权,可以继续访问目标操作callback.onSuccess && callback.onSuccess()} else {// 用户拒绝授权,提示用户必须授权才能访问当前页面的功能,并引导用户到系统设置中打开相应的权限callback.onFailed && callback.onFailed();}}// 授权成功}).catch((err: BusinessError) => {console.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`);});}public static gotoSystemSetting() {const buildInfo = bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);const context = getContext() as common.UIAbilityContext;context.startAbility({bundleName: 'com.huawei.hmos.settings',abilityName: 'com.huawei.hmos.settings.MainAbility',uri: "application_info_entry",parameters: {pushParams: buildInfo.name}});}
}interface Callbacks {onSuccess?: () => voidonFailed?: () => void
}
5.2 工具类申请权限
PermissionsUtils.requestPermissionsFromUser(['ohos.permission.MICROPHONE'], {onSuccess: () => {console.info("申请麦克风权限成功")},onFailed:()=>{console.info("申请麦克风权限失败")}
})
5.3 工具类检查权限
PermissionsUtils.checkPermissionGrant("ohos.permission.MICROPHONE",{onSuccess:()=>{console.info("已经有权限,可以访问目标操作")},onFailed:()=>{console.info("没有权限,去系统设置打开权限")PermissionsUtils.gotoSystemSetting()}
})
6.申请地理位置权限
6.1 配置地理位置权限
ohos.permission.APPROXIMATELY_LOCATION
允许应用获取设备模糊位置信息。
权限级别:normal
授权方式:user_grant
起始版本:9
先在modeule.json5配置文件中加入权限配置
{"module" : {"requestPermissions":[{"name": "ohos.permission.APPROXIMATELY_LOCATION","reason": "$string:permission_location","usedScene": {"abilities": ["EntryAbility"]}}]}
}
6.2 获取地理位置经纬度
import { geoLocationManager } from '@kit.LocationKit';
import { PermissionsUtils } from '../utils/PermissionsUtils';@Entry
@Component
struct Index {@State result: geoLocationManager.Location = {} as geoLocationManager.Location@State lcoationPermission: boolean = false//进入页面,时申请位置权限aboutToAppear(): void {PermissionsUtils.requestPermissionsFromUser(['ohos.permission.APPROXIMATELY_LOCATION'],{onSuccess: () => {this.lcoationPermission = true}})}build() {Column() {//点击按钮时,获取经纬度Button('获取经纬度').onClick(async () => {if (this.lcoationPermission) {this.result = await geoLocationManager.getCurrentLocation()}})Text('经度:' + this.result.latitude)Text('纬度:' + this.result.longitude)}.height('100%')}
}