diff --git a/zh-cn/application-dev/security/accesstoken-guidelines.md b/zh-cn/application-dev/security/accesstoken-guidelines.md index f3ea0f6e7cf313ad3be75cbe0b7d1e1a47ee05c5..f2ac38c6167371871dfef3e413ddcc7ac0d911ec 100644 --- a/zh-cn/application-dev/security/accesstoken-guidelines.md +++ b/zh-cn/application-dev/security/accesstoken-guidelines.md @@ -1,71 +1,49 @@ -# 访问控制授权申请指导 +# 访问控制授权申请 ## 场景介绍 -以下示例代码基于此场景假设:应用因为应用核心功能诉求,需要申请权限"ohos.permission.PERMISSION1"和权限"ohos.permission.PERMISSION2"。 +[应用的APL(Ability Privilege Level)等级](accesstoken-overview.md#应用apl等级说明)分为`normal`、`system_basic`和`system_core`三个等级,默认情况下,应用的APL等级都为`normal`等级。[权限类型](accesstoken-overview.md#权限类型说明)分为`system_grant`和`user_grant`两种类型。应用可申请的权限项参见[应用权限列表](permission-list.md)。 -- 应用的APL等级为normal。 -- 权限"ohos.permission.PERMISSION1"的权限等级为normal,权限类型为system_grant。 -- 权限"ohos.permission.PERMISSION2"的权限等级为system_basic, 权限类型为user_grant。 +本文将从如下场景分别介绍: -> **注意:** -> -> 当前场景下,应用申请的权限包括了user_grant权限,对这部分user_grant权限,可以先通过权限校验,判断当前调用者是否具备相应权限。 -> -> 当权限校验结果显示当前应用尚未被授权该权限时,再通过动态弹框授权方式给用户提供手动授权入口。 +- [配置文件权限声明](#配置文件权限声明) +- [ACL方式声明](#acl方式声明) +- [向用户申请授权](#向用户申请授权) +- [user_grant权限预授权](#user_grant权限预授权) -应用可申请的权限,可查询[应用权限列表](permission-list.md) +## 配置文件权限声明 -## 接口说明 +应用需要在工程配置文件中,对需要的权限逐个声明,未在配置文件中声明的权限,应用将无法获得授权。OpenHarmony提供了两种应用模型,分别为FA模型和Stage模型,更多信息可以参考[应用模型解读](../application-models/application-model-description.md)。不同的应用模型的应用包结构不同,所使用的配置文件不同。 -以下仅列举本指导使用的接口,不同模型下使用的拉起权限弹窗的接口有差异,更多说明可以查阅[完整示例](##完整示例)。 +> **说明**: +> +> 应用默认的APL等级为`normal`,当应用需要申请`system_basic`和`system_core`等级时,除了在配置文件中进行权限声明之外,还需要通过[ACL方式](#acl方式声明)进行声明使用。 -### FA模型 -| 接口名 | 描述 | -| ------------------------------------------------------------ | --------------------------------------------------- | -| requestPermissionsFromUser(permissions: Array<string>, requestCallback: AsyncCallback<PermissionRequestResult>) : void; | 拉起弹窗请求用户授权。 | -> 详细可查阅[API参考](../reference/apis/js-apis-ability-context.md) +配置文件标签说明如下表所示。 +| 标签 | 是否必填 | 说明 | +| --------- | -------- | ------------------------------------------------------------ | +| name | 是 | 权限名称。 | +| reason | 否 | 描述申请权限的原因。
> **说明**:当申请的权限为user_grant权限时,此字段必填。 | +| usedScene | 否 | 描述权限使用的场景和时机。
> **说明**:当申请的权限为user_grant权限时,此字段必填。 | +| abilities | 否 | 标识需要使用到该权限的Ability,标签为数组形式。
**适用模型**:Stage模型 | +| ability | 否 | 标识需要使用到该权限的Ability,标签为数组形式。
**适用模型**:FA模型 | +| when | 否 | 标识权限使用的时机,值为`inuse/always`。
- inuse:表示为仅允许前台使用。
- always:表示前后台都可使用。 | ### Stage模型 -| 接口名 | 描述 | -| ------------------------------------------------------------ | --------------------------------------------------- | -| requestPermissionsFromUser(context: Context, permissions: Array<Permissions>, requestCallback: AsyncCallback<PermissionRequestResult>) : void; | 拉起弹窗请求用户授权。 | -> 详细可查阅[API参考](../reference/apis/js-apis-abilityAccessCtrl.md) - -## 权限申请声明 - -应用需要在工程配置文件中,对需要的权限逐个声明,没有在配置文件中声明的权限,应用将无法获得授权。OpenHarmony提供了两种应用模型,分别为FA模型和Stage模型,更多信息可以参考[应用模型解读](../application-models/application-model-description.md)。 - -不同的应用模型的应用包结构不同,所使用的配置文件不同,请开发者在申请权限时注意区分。 - -配置文件标签说明如下表。 - -| 标签 | 说明 | -| --------- | ------------------------------------------------------------ | -| name | 权限名称。 | -| reason | 当申请的权限为user_grant权限时,此字段必填,描述申请权限的原因。 | -| usedScene | 当申请的权限为user_grant权限时,此字段必填,描述权限使用的场景和时机。 | -| ability | 标识需要使用到该权限的Ability,标签为数组形式。
**适用模型:** FA模型 | -| abilities | 标识需要使用到该权限的Ability,标签为数组形式。
**适用模型:** Stage模型 | -| when | 标识权限使用的时机,值为"inuse/always",表示为仅允许前台使用和前后台都可使用。 | - -### FA模型 - -使用FA模型的应用,需要在config.json文件中声明权限。 - -**示例:** +使用Stage模型的应用,需要在[module.json5配置文件](../quick-start/module-configuration-file.md)中声明权限。 ```json { "module" : { - "reqPermissions":[ + // ... + "requestPermissions":[ { "name" : "ohos.permission.PERMISSION1", "reason": "$string:reason", "usedScene": { - "ability": [ + "abilities": [ "FormAbility" ], "when":"inuse" @@ -75,7 +53,7 @@ "name" : "ohos.permission.PERMISSION2", "reason": "$string:reason", "usedScene": { - "ability": [ + "abilities": [ "FormAbility" ], "when":"always" @@ -86,21 +64,20 @@ } ``` -### Stage模型 - -使用Stage模型的应用,需要在module.json5文件中声明权限。 +### FA模型 -**示例:** +使用FA模型的应用,需要在config.json配置文件中声明权限。 ```json { "module" : { - "requestPermissions":[ + // ... + "reqPermissions":[ { "name" : "ohos.permission.PERMISSION1", "reason": "$string:reason", "usedScene": { - "abilities": [ + "ability": [ "FormAbility" ], "when":"inuse" @@ -110,7 +87,7 @@ "name" : "ohos.permission.PERMISSION2", "reason": "$string:reason", "usedScene": { - "abilities": [ + "ability": [ "FormAbility" ], "when":"always" @@ -123,114 +100,239 @@ ## ACL方式声明 -如上述示例所示,权限"ohos.permission.PERMISSION2"的权限等级为system_basic,高于此时应用的APL等级,开发者的最佳做法是使用ACL方式。 - -在配置文件声明的基础上,应用还需要在Profile文件中声明不满足申请条件部分的权限。Profile文件的字段说明可参考[HarmonyAppProvision配置文件的说明](app-provision-structure.md)。 +应用在申请`system_basic`和`system_core`等级权限时,高于应用默认的`normal`等级。当应用需要申请权限项的等级高于应用默认的等级时,需要通过ACL方式进行声明使用。 -该场景中,开发者应该在字段"acls"中做声明如下: +例如应用在申请访问用户公共目录下音乐类型的文件,需要申请` ohos.permission.WRITE_AUDIO`权限,该权限为`system_basic`等级;以及应用在申请截取屏幕图像功能,该权限为`system_core`等级,需要申请` ohos.permission.CAPTURE_SCREEN`权限。此时需要将相关权限项配置到[HarmonyAppProvision配置文件](app-provision-structure.md)的`acl`字段中。 ```json { - "acls": { - "allowed-acls": [ - "ohos.permission.PERMISSION2" - ] - } + // ... + "acls":{ + "allowed-acls":[ + "ohos.permission.WRITE_AUDIO", + "ohos.permission.CAPTURE_SCREEN" + ] + } } ``` -## 申请授权user_grant权限 - -在前期的权限声明步骤后,在安装过程中系统会对system_grant类型的权限进行权限预授权,而user_grant类型权限则需要用户进行手动授权。 +## 向用户申请授权 -所以,应用在调用受"ohos.permission.PERMISSION2"权限保护的接口前,需要先校验应用是否已经获取该权限。 +当应用需要访问用户的隐私信息或使用系统能力时,例如获取位置信息、访问日历、使用相机拍摄照片或录制视频等,应该向用户请求授权。这需要使用 `user_grant` 类型权限。在此之前,应用需要进行权限校验,以判断当前调用者是否具备所需的权限。如果权限校验结果表明当前应用尚未被授权该权限,则应使用动态弹框授权方式,为用户提供手动授权的入口。示意效果如下图所示。 -如果校验结果显示,应用已经获取了该权限,那么应用可以直接访问该目标接口,否则,应用需要通过动态弹框先申请用户授权,并根据授权结果进行相应处理,处理方式可参考[访问控制开发概述](accesstoken-overview.md)。 +图1 向用户申请授权 +![](figures/permission-read_calendar.png) -> **注意:** +> **说明**: > -> 不能把之前授予的状态持久化,每次访问受目标权限保护的接口前,都应该调用requestPermissionsFromUser接口请求权限,因为用户在动态授予后可能通过设置取消应用的权限。 +> 每次访问受目标权限保护的接口之前,都需要使用 [requestPermissionsFromUser()](../reference/apis/js-apis-abilityAccessCtrl.md#requestpermissionsfromuser9) 接口请求相应的权限。用户可能在动态授予权限后通过系统设置来取消应用的权限,因此不能将之前授予的授权状态持久化。 + +### Stage模型 -## 完整示例 +以允许应用读取日历信息为例进行说明。 + +1. 申请`ohos.permission.READ_CALENDAR`权限,配置方式请参见[访问控制授权申请](#配置文件权限声明)。 + +2. 校验当前是否已经授权。 + + 在进行权限申请之前,需要先检查当前应用程序是否已经被授予了权限。可以通过调用[checkAccessToken()](../reference/apis/js-apis-abilityAccessCtrl.md#checkaccesstoken9)方法来校验当前是否已经授权。如果已经授权,则可以直接访问目标操作,否则需要进行下一步操作,即向用户申请授权。 + + ```ts + import bundleManager from '@ohos.bundle.bundleManager'; + import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl'; + + async function checkAccessToken(permission: Permissions): Promise { + let atManager = abilityAccessCtrl.createAtManager(); + let grantStatus: abilityAccessCtrl.GrantStatus; + + // 获取应用程序的accessTokenID + let tokenId: number; + 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 (err) { + console.error(`getBundleInfoForSelf failed, code is ${err.code}, message is ${err.message}`); + } + + // 校验应用是否被授予权限 + try { + grantStatus = await atManager.checkAccessToken(tokenId, permission); + } catch (err) { + console.error(`checkAccessToken failed, code is ${err.code}, message is ${err.message}`); + } + + return grantStatus; + } + + async function checkPermissions(): Promise { + const permissions: Array = ['ohos.permission.READ_CALENDAR']; + let grantStatus: abilityAccessCtrl.GrantStatus = await checkAccessToken(permissions[0]); + + if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) { + // 已经授权,可以继续访问目标操作 + } else { + // 申请日历权限 + } + } + ``` + +3. 动态向用户申请授权。 + + 动态向用户申请权限是指在应用程序运行时向用户请求授权的过程。可以通过调用[requestPermissionsFromUser()](../reference/apis/js-apis-abilityAccessCtrl.md#requestpermissionsfromuser9)方法来实现。该方法接收一个权限列表参数,例如位置、日历、相机、麦克风等。用户可以选择授予权限或者拒绝授权。 + + 可以在UIAbility的`onWindowStageCreate()`回调中调用[requestPermissionsFromUser()](../reference/apis/js-apis-abilityAccessCtrl.md#requestpermissionsfromuser9)方法来动态申请权限,也可以根据业务需要在UI中向用户申请授权。 + + 在UIAbility中向用户申请授权。 + + ```typescript + import UIAbility from '@ohos.app.ability.UIAbility'; + import window from '@ohos.window'; + import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl'; + + const permissions: Array = ['ohos.permission.READ_CALENDAR']; + + export default class EntryAbility extends UIAbility { + // ... + + onWindowStageCreate(windowStage: window.WindowStage) { + // Main window is created, set main page for this ability + let context = this.context; + let atManager = abilityAccessCtrl.createAtManager(); + // requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗 + + atManager.requestPermissionsFromUser(context, permissions).then((data) => { + let grantStatus: Array = 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}`); + }) + + // ... + } + } + ``` + + 在UI中向用户申请授权。 + ```typescript + import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl'; + import common from '@ohos.app.ability.common'; + + const permissions: Array = ['ohos.permission.READ_CALENDAR']; + + @Entry + @Component + struct Index { + reqPermissionsFromUser(permissions: Array): void { + let context = getContext(this) as common.UIAbilityContext; + let atManager = abilityAccessCtrl.createAtManager(); + // requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗 + atManager.requestPermissionsFromUser(context, permissions).then((data) => { + let grantStatus: Array = 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}`); + }) + } + + // 页面展示 + build() { + // ... + } + } + ``` + +4. 处理授权结果。 + + 调用[requestPermissionsFromUser()](../reference/apis/js-apis-abilityAccessCtrl.md#requestpermissionsfromuser9)方法后,应用程序将等待用户授权的结果。如果用户授权,则可以继续访问目标操作。如果用户拒绝授权,则需要提示用户必须授权才能访问当前页面的功能,并引导用户到系统设置中打开相应的权限。 + + ```ts + function openPermissionsInSystemSettings(): void { + let context = getContext(this) as common.UIAbilityContext; + let wantInfo = { + action: 'action.settings.app.info', + parameters: { + settingsParamBundleName: 'com.example.myapplication' // 打开指定应用的详情页面 + } + } + context.startAbility(wantInfo).then(() => { + // ... + }).catch((err) => { + // ... + }) + } + ``` -请求用户授权权限的开发步骤为: +### FA模型 -1. 获取ability的上下文context。 -2. 调用requestPermissionsFromUser接口请求权限。运行过程中,该接口会根据应用是否已获得目标权限决定是否拉起动态弹框请求用户授权。 -3. 根据requestPermissionsFromUser接口返回值判断是否已获取目标权限。如果当前已经获取权限,则可以继续正常访问目标接口。 +通过调用[requestPermissionsFromUser()](../reference/apis/js-apis-inner-app-context.md#contextrequestpermissionsfromuser7)接口向用户动态申请授权。 -### FA模型下的示例代码 ```js - import featureAbility from '@ohos.ability.featureAbility'; +import featureAbility from '@ohos.ability.featureAbility'; - reqPermissions() { +reqPermissions() { let context = featureAbility.getContext(); let array:Array = ["ohos.permission.PERMISSION2"]; //requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗 context.requestPermissionsFromUser(array, 1).then(function(data) { - console.log("data type:" + typeof(data)); - console.log("data:" + data); - console.log("data permissions:" + data.permissions); - console.log("data result:" + data.authResults); + console.log("data:" + JSON.stringify(data)); + console.log("data permissions:" + JSON.stringify(data.permissions)); + console.log("data result:" + JSON.stringify(data.authResults)); }, (err) => { - console.error('Failed to start ability', err.code); + console.error('Failed to start ability', err.code); }); - } - +} ``` -> **说明:** -> FA模型的动态授权申请接口的使用详见[API参考](../reference/apis/js-apis-ability-context.md)。 - -### stage 模型下的示例代码 -```js - import abilityAccessCtrl from '@ohos.abilityAccessCtrl'; - - //ability的onWindowStageCreate生命周期 - onWindowStageCreate() { - var context = this.context - var AtManager = abilityAccessCtrl.createAtManager(); - //requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗 - AtManager.requestPermissionsFromUser(context, ["ohos.permission.CAMERA"]).then((data) => { - console.log("data type:" + typeof(data)); - console.log("data:" + data); - console.log("data permissions:" + data.permissions); - console.log("data result:" + data.authResults); - }).catch((err) => { - console.error('Failed to start ability', err.code); - }) - } +## user_grant权限预授权 +应用在申请`user_grant`类型的权限默认未授权,需要通过拉起弹框由用户确认是否授予该权限。对于一些预制应用,不希望出现弹窗申请`user_grant`类型的权限,例如系统相机应用需要使用麦克风` ohos.permission.MICROPHONE`等权限,需要对麦克风等权限进行预授权,可以通过预授权的方式完成`user_grant`类型权限的授权。[预置配置文件](https://gitee.com/openharmony/vendor_hihope/blob/master/rk3568/preinstall-config/install_list_permissions.json)在设备上的路径为`/system/etc/app/install_list_permission.json`,设备开机启动时会读取该配置文件,在应用安装会对在文件中配置的`user_grant`类型权限授权。预授权配置文件字段内容包括`bundleName`、`app_signature`和`permissions`。 -``` -> **说明:** -> stage模型的动态授权申请接口的使用详见[API参考](../reference/apis/js-apis-abilityAccessCtrl.md)。 +- `bundleName`字段配置为应用的Bundle名称。 +- `app_signature`字段配置为应用的指纹信息。指纹信息的配置参见[应用特权配置指南](../../device-dev/subsystems/subsys-app-privilege-config-guide.md#install_list_capabilityjson中配置)。 +- `permissions`字段中`name`配置为需要预授权的`user_grant`类型的权限名;`permissions`字段中`userCancellable`表示为用户是否能够取消该预授权,配置为true,表示支持用户取消授权,为false则表示不支持用户取消授权。 -## user_grant权限预授权 -当前正常情况下,user_grant类型的权限默认不授权,需要时应通过拉起弹框由用户确认是否授予。对于一些预置应用,比如截屏应用,不希望出现弹框,则可以通过预授权的方式完成user_grant类型权限的授权。[预置配置文件](https://gitee.com/openharmony/vendor_hihope/blob/master/rk3568/preinstall-config/install_list_permissions.json)在设备上的路径为system/etc/app/install_list_permission.json,设备开机启动时会读取该配置文件,在应用安装会对在文件中配置的user_grant类型权限授权。当前仅支持预置应用配置该文件。 -预授权配置文件字段内容包括bundleName、app_signature、permissions。 -1. 这里的权限仅对user_grant类型的权限生效[查看权限等级和类型](permission-list.md)。 -2. userCancellable配置为true,表示支持用户取消授权,为false则表示不支持用户取消授权。 +> **说明**:当前仅支持预置应用配置该文件。 ```json [ + // ... { - "bundleName": "com.ohos.myapplication", // 包名 - "app_signature":[], // 指纹信息 + "bundleName": "com.example.myapplication", // Bundle名称 + "app_signature": ["****"], // 指纹信息 "permissions":[ { - "name":"xxxx", // 权限名,不可缺省 - "userCancellable":false // 用户不可取消授权,不可缺省 + "name": "ohos.permission.PERMISSION_X", // user_grant类型预授权的权限名 + "userCancellable": false // 用户不可取消授权 }, { - "name":"yyy", // 权限名,不可缺省 - "userCancellable":true // 用户可取消授权,不可缺省 + "name": "ohos.permission.PERMISSION_Y", // user_grant类型预授权的权限名 + "userCancellable": true // 用户可取消授权 } ] } ] ``` + ## 相关实例 针对访问控制,有以下相关实例可供参考: -- [`AbilityAccessCtrl`:访问权限控制(ArkTS)(Full SDK)(API8)(Full SDK)](https://gitee.com/openharmony/applications_app_samples/tree/OpenHarmony-3.2-Beta5/Safety/AbilityAccessCtrl) -- [为应用添加运行时权限(ArkTS)(Full SDK)(API 9)](https://gitee.com/openharmony/codelabs/tree/master/Ability/AccessPermission) \ No newline at end of file +- [AbilityAccessCtrl:访问权限控制(ArkTS)(Full SDK)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SystemFeature/Security/AbilityAccessCtrl) +- [为应用添加运行时权限(ArkTS)(Full SDK)(API9)](https://gitee.com/openharmony/codelabs/tree/master/Ability/AccessPermission) \ No newline at end of file