权限管理
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 配置网络权限
bash
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
}
}
3.2 使用网络
bash
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的配置文件中逐个声明需要的权限,否则应用将无法获取授权
bash
{
"module" : {
// ...
"requestPermissions": [
{
"name": "ohos.permission.MICROPHONE", //权限名称(这里是麦克风权限)
"reason": "$string:MICROPHONE_REASON", //原因
"usedScene": { //该权限由谁发起
"abilities": ["EntryAbility"], //由EntryAbility发起
"when": "always" //发起时机(inuse使用时允许、always总是允许)
}
}
],
}
}
4.2 动态申请麦克风授权
动态申请授权指的是拉起系统授权弹窗,让用户选择"允许"或"禁止",只有用户选择"允许"的情况下,才可以执行目标操作。我们以获取麦克风权限为例,让用户选择是否允许授权
bash
let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager()
atManager.requestPermissionsFromUser(getContext(this),['ohos.permission.MICROPHONE']).then((data) => {
let grantStatus: Array<number> = data.authResults
let 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()方法来校验当前是否已经授权,如果已经授权,则可以直接访问目标操作;如果没有授权则提醒用户到系统设置手动打开应用权限。
bash
//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("没有授权,不能执行目标操作")
}
下面是打开系统设置的代码
bash
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
}
})
}
5. 封装权限工具类
5.1 权限工具类
bash
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_DENIED
try {
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.UIAbilityContext
let 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?: () => void
onFailed?: () => void
}
5.2 工具类申请权限
bash
PermissionsUtils.requestPermissionsFromUser(['ohos.permission.MICROPHONE'], {
onSuccess: () => {
console.info("申请麦克风权限成功")
},
onFailed:()=>{
console.info("申请麦克风权限失败")
}
})
5.3 工具类检查权限
bash
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配置文件中加入权限配置
bash
{
"module" : {
"requestPermissions":[
{
"name": "ohos.permission.APPROXIMATELY_LOCATION",
"reason": "$string:permission_location",
"usedScene": {
"abilities": ["EntryAbility"]
}
}
]
}
}
6.2 获取地理位置经纬度
bash
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%')
}
}