一.权限等级
根据接口所涉数据的敏感程度或所涉能力的安全威胁影响,ATM模块定义了不同开放范围的权限等级来保护用户隐私。
应用APL等级说明
元能力权限等级APL(Ability Privilege Level)指的是应用的权限申请优先级的定义,不同APL等级的应用能够申请的权限等级不同。
应用的等级可以分为三个等级,分别是:
| APL级别 | 说明 |
|---|---|
| system_core等级 | 该等级的应用服务提供操作系统核心能力。 |
| system_basic等级 | 该等级的应用服务提供系统基础服务。 |
| normal等级 | 普通应用。 |
默认情况下,应用的APL等级都为normal等级。
权限等级说明
根据权限对于不同等级应用有不同的开放范围,权限类型对应分为以下三种,等级依次提高。
-
normal权限(重点)
normal 权限允许应用访问超出默认规则外的普通系统资源。这些系统资源的开放(包括数据和功能)对用户隐私以及其他应用带来的风险很小。
该类型的权限仅向APL等级为normal及以上的应用开放。
-
system_basic权限
system_basic权限允许应用访问操作系统基础服务相关的资源。这部分系统基础服务属于系统提供或者预置的基础功能,比如系统设置、身份认证等。这些系统资源的开放对用户隐私以及其他应用带来的风险较大。
该类型的权限仅向APL等级为system_basic及以上的应用开放。
-
system_core权限
system_core权限涉及到开放操作系统核心资源的访问操作。这部分系统资源是系统最核心的底层服务,如果遭受破坏,操作系统将无法正常运行。
鉴于该类型权限对系统的影响程度非常大,目前暂不向任何三方应用开放。
二.权限类型
权限类型说明
根据授权方式的不同,权限类型可分为system_grant(系统授权)和user_grant(用户授权)。
-
system_grant
system_grant指的是系统授权类型,在该类型的权限许可下,应用被允许访问的数据不会涉及到用户或设备的敏感信息,应用被允许执行的操作不会对系统或者其他应用产生大的不利影响。
如果在应用中申请了system_grant权限,那么系统会在用户安装应用时,自动把相应权限授予给应用。应用需要在应用商店的详情页面,向用户展示所申请的system_grant权限列表。
-
user_grant(重点)
user_grant指的是用户授权类型,在该类型的权限许可下,应用被允许访问的数据将会涉及到用户或设备的敏感信息,应用被允许执行的操作可能对系统或者其他应用产生严重的影响。
该类型权限不仅需要在安装包中申请权限,还需要在应用动态运行时,通过发送弹窗的方式请求用户授权。在用户手动允许授权后,应用才会真正获取相应权限,从而成功访问操作目标对象。
比如说,在权限定义列表中,麦克风和摄像头对应的权限都是属于用户授权权限,列表中给出了详细的权限使用理由。
应用需要在应用商店的详情页面,向用户展示所申请的user_grant权限列表。
不同权限类型的授权流程
如果应用需要获取目标权限,那么需要先进行权限申请。
- 权限申请
开发者需要在
module.json5配置文件中声明目标权限。 - 权限授权
- 如果目标权限是system_grant类型,开发者在进行权限申请后,系统会在安装应用时自动为其进行权限预授予,开发者不需要做其他操作即可使用权限。
- 如果目标权限是user_grant类型,开发者在进行权限申请后,在运行时触发动态弹窗,请求用户授权。
user_grant权限请求授权的步骤详解
在应用需要获取user_grant权限时,请完成以下步骤:
- 在
module.json5配置文件中,声明应用需要请求的权限。 - 将应用中需要申请权限的目标对象与对应目标权限进行关联,让用户明确地知道,哪些操作需要用户向应用授予指定的权限。
- 运行应用时,在用户触发访问操作目标对象时应该调用接口,精准触发动态授权弹框。该接口的内部会检查当前用户是否已经授权应用所需的权限,如果当前用户尚未授予应用所需的权限,该接口会拉起动态授权弹框,向用户请求授权。
- 检查用户的授权结果,确认用户已授权才可以进行下一步操作。
注意事项:
- 每次执行需要目标权限的操作时,应用都必须检查自己是否已经具有该权限。
- 如需检查用户是否已向您的应用授予特定权限,可以使用函数
checkAccessToken,此方法会返回PERMISSION_GRANTED(已授权)或PERMISSION_DENIED(未授权)。 - user_grant权限授权要基于用户可知可控的原则,需要应用在运行时主动调用系统动态申请权限的接口,系统弹框由用户授权,用户结合应用运行场景的上下文,识别出应用申请相应敏感权限的合理性,从而做出正确的选择。
- 即使用户向应用授予过请求的权限,应用在调用受此权限管控的接口前,也应该先检查自己有无此权限,而不能把之前授予的状态持久化,因为用户在动态授予后还可以通过设置取消应用的权限。
三.module.json5配置文件权限声明
配置文件标签说明如下表所示。
| 标签 | 是否必填 | 说明 |
|---|---|---|
| name | 是 | 权限名称。 |
| reason | 否 | 描述申请权限的原因。> 说明:当申请的权限为user_grant权限时,此字段必填。 |
| usedScene | 否 | 描述权限使用的场景和时机。> 说明:当申请的权限为user_grant权限时,此字段必填。 |
| abilities | 否 | 标识需要使用到该权限的Ability,标签为数组形式 |
| when | 否 | 标识权限使用的时机,值为inuse/always。- inuse:表示为仅允许前台使用。- always:表示前后台都可使用。 |
{
"module": {
"requestPermissions":[
{
"name" : "ohos.permission.READ_CALENDAR",// 权限名称
"reason": "$string:ControlPermissions_rili",//申请权限的理由
// 使用场景和时机,当申请的权限为user_grant权限时,此字段必填
"usedScene": {
// 需要使用到该权限的ability
"abilities": [
"EntryAbility"
],
/*
* 标识权限使用的时机,值为inuse/always。
* - inuse:表示为仅允许前台使用。
* - always:表示前后台都可使用
*/
"when":"inuse"
}
}
]
}
四.向用户申请权限
APP启动时在Ability中申请
1.导入访问控制管理模块,并定义好权限数组
import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';
const permissions: Array<Permissions> = [
'ohos.permission.READ_CALENDAR',//日历
'ohos.permission.MICROPHONE',//麦克风
'ohos.permission.LOCATION',//位置
'ohos.permission.CAMERA'//相机
];
2.自定义一个权限申请方法
applyForPermission() {
let context = this.context;
let atManager = abilityAccessCtrl.createAtManager();
// requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗
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) {
// 用户授权,可以继续访问目标操作
} else {
// 用户拒绝授权,提示用户必须授权才能访问当前页面的功能,并引导用户到系统设置中打开相应的权限
return;
}
}
// 授权成功
}).catch((err) => {
console.error(`requestPermissionsFromUser failed, code is ${err.code}, message is ${err.message}`);
})
}
3.在onWindowStageCreate中调用
onWindowStageCreate(windowStage: window.WindowStage) {
this.applyForPermission()
}
动态向用户申请
1.导入需要模块并定义权限数组
// 访问控制管理模块
import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';
// 公共模块
import common from '@ohos.app.ability.common';
// 应用信息查询
import bundleManager from '@ohos.bundle.bundleManager';
const permissions: Array<Permissions> = [
'ohos.permission.READ_CALENDAR',//日历
'ohos.permission.MICROPHONE',//麦克风
'ohos.permission.LOCATION',//位置
'ohos.permission.CAMERA'//相机
];
2.在需要开启权限时动态申请
Button('申请权限').onClick((event: ClickEvent) => {
this.applyForPermission()
})
// 申请权限
applyForPermission(): void {
// 获取UIAbility的上下文环境
let context = getContext(this) as common.UIAbilityContext;
// 获取访问控制模块对象
let atManager = abilityAccessCtrl.createAtManager();
// requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗
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) {
// 用户授权,可以继续访问目标操作
} else {
// 用户拒绝授权,提示用户必须授权才能访问当前页面的功能,并引导用户到系统设置中打开相应的权限
return;
}
}
// 授权成功
}).catch((err) => {
console.error(`requestPermissionsFromUser failed, code is ${err.code}, message is ${err.message}`);
})
}
完整的权限申请流程
1.获取授权状态
// 获取授权状态
async function checkAccessToken(permission: Permissions): Promise<abilityAccessCtrl.GrantStatus> {
let atManager = abilityAccessCtrl.createAtManager();
// 授权状态的枚举
/**
* PERMISSION_DENIED -1 表示未授权。
* PERMISSION_GRANTED 0 表示已授权。
* */
let grantStatus: abilityAccessCtrl.GrantStatus;
// 获取应用程序的accessTokenID
let tokenId: number;
try {
/**
* 以异步方法根据给定的bundleFlags获取当前应用的BundleInfo,使用Promise形式返回结果。
* GET_BUNDLE_INFO_WITH_APPLICATION:用于获取包含applicationInfo的bundleInfo
* */
let bundleInfo: bundleManager.BundleInfo = await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);
// 获取有关应用程序的配置信息
let appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo;
tokenId = appInfo.accessTokenId;
} catch (err) {
console.error(`getBundleInfoForSelf failed, code is ${err.code}, message is ${err.message}`);
}
// 校验应用是否被授予权限
try {
// 校验应用是否授予权限,使用Promise方式异步返回结果
grantStatus = await atManager.checkAccessToken(tokenId, permission);
} catch (err) {
console.error(`checkAccessToken failed, code is ${err.code}, message is ${err.message}`);
}
return grantStatus;
}
2.判断是否已授权
// 判断是否已授权
async function checkPermissions(permission: Permissions): Promise<void> {
let grantStatus: abilityAccessCtrl.GrantStatus = await checkAccessToken(permission);
if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
xzqAlert(true)
} else {
xzqAlert(false)
}
}
3.根据授权状态进行弹窗提示
// 弹窗提示
function xzqAlert(per:boolean) {
let msg = per ? '已授权' : '未授权'
AlertDialog.show(
{
title: '提示',
message: msg,
autoCancel: true,
alignment: DialogAlignment.Bottom,
offset: { dx: 0, dy: -20 },
gridCount: 3,
confirm: {
value: '确定',
action: () => {
if (!per) {
openPermissionsInSystemSettings()
}
}
}
}
)
}
4.未授权则跳到授权开启页
// 跳转到对应的权限开启页
function openPermissionsInSystemSettings(): void {
let context = getContext(this) as common.UIAbilityContext;
let wantInfo = {
action: 'action.settings.app.info',
parameters: {
settingsParamBundleName: 'com.example.controlpermissions' // 打开指定应用的详情页面
}
}
context.startAbility(wantInfo).then(() => {
}).catch((err) => {
})
}
五.权限列表
ohos.permission.READ_CALENDAR
允许应用读取日历信息。
权限级别:normal
授权方式:user_grant
ACL使能:TRUE
ohos.permission.MICROPHONE
允许应用使用麦克风。
权限级别:normal
授权方式:user_grant
ACL使能:TRUE
ohos.permission.LOCATION
允许应用获取设备位置信息。
权限级别:normal
授权方式:user_grant
ACL使能:TRUE
ohos.permission.CAMERA
允许应用使用相机拍摄照片和录制视频。
权限级别:normal
授权方式:user_grant
ACL使能:TRUE