accesstoken-guidelines.md 16.8 KB
Newer Older
Z
zengyawen 已提交
1
# 访问控制授权申请指导
L
l00520400 已提交
2 3 4

## 场景介绍

5
[应用的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)
L
l00520400 已提交
6

7
本文将从如下场景分别介绍:
8

Z
zengyawen 已提交
9 10 11 12 13 14 15 16 17 18 19 20
- [访问控制授权申请指导](#访问控制授权申请指导)
  - [场景介绍](#场景介绍)
  - [配置文件权限声明](#配置文件权限声明)
    - [Stage模型](#stage模型)
    - [FA模型](#fa模型)
    - [权限使用理由的文案内容规范](#权限使用理由的文案内容规范)
  - [ACL方式声明](#acl方式声明)
  - [向用户申请授权](#向用户申请授权)
    - [Stage模型](#stage模型-1)
    - [FA模型](#fa模型-1)
  - [user\_grant权限预授权](#user_grant权限预授权)
  - [相关实例](#相关实例)
21

22
## 配置文件权限声明
Z
zengyawen 已提交
23

24
应用需要在项目的配置文件中逐个声明所需的权限,否则应用将无法获取授权。
25

zyjhandsome's avatar
zyjhandsome 已提交
26 27
> **说明**:
>
28
> 应用在申请`system_basic`和`system_core`等级权限时,需要提升权限等级,因为应用默认的权限等级为`normal`。如果应用需要申请高于默认等级的权限,除了在配置文件中进行声明之外,还需要通过[ACL方式](#acl方式声明)进行声明使用。
29

30
配置文件标签说明如下表所示。
31

32 33 34
| 标签      | 是否必填 | 说明                                                         |
| --------- | -------- | ------------------------------------------------------------ |
| name      | 是       | 权限名称。                                                   |
Z
update  
zengyawen 已提交
35 36
| reason    | 否       | 描述申请权限的原因,可参考[权限使用理由的文案内容规范](#权限使用理由的文案内容规范)<br/> > **说明**:当申请的权限为user_grant权限时,此字段必填。 |
| usedScene | 否       | 描述权限使用的场景和时机。<br/> > **说明**:当申请的权限为user_grant权限时,此字段必填。 |
37 38
| abilities | 否       | 标识需要使用到该权限的Ability,标签为数组形式。<br/>**适用模型**:Stage模型 |
| ability   | 否       | 标识需要使用到该权限的Ability,标签为数组形式。<br/>**适用模型**:FA模型 |
Z
zengyawen 已提交
39
| when      | 否       | 标识权限使用的时机,值为`inuse/always`<br/>- inuse:表示为仅允许前台使用。<br/>- always:表示前后台都可使用。 |
40

41
### Stage模型
42

43
使用Stage模型的应用,需要在[module.json5配置文件](../quick-start/module-configuration-file.md)中声明权限。
44

F
update  
fanchenxuan 已提交
45 46
```json
{
47
  "module" : {
48 49
    // ...
    "requestPermissions":[
50 51 52 53
      {
        "name" : "ohos.permission.PERMISSION1",
        "reason": "$string:reason",
        "usedScene": {
54
          "abilities": [
55 56 57 58 59 60 61 62 63
            "FormAbility"
          ],
          "when":"inuse"
        }
      },
      {
        "name" : "ohos.permission.PERMISSION2",
        "reason": "$string:reason",
        "usedScene": {
64
          "abilities": [
65 66 67 68 69 70 71
            "FormAbility"
          ],
          "when":"always"
        }
      }
    ]
  }
F
update  
fanchenxuan 已提交
72 73 74
}
```

75
### FA模型
F
update  
fanchenxuan 已提交
76

77
使用FA模型的应用,需要在config.json配置文件中声明权限。
F
update  
fanchenxuan 已提交
78

79 80
```json
{
81
  "module" : {
82 83
    // ...
    "reqPermissions":[
84 85 86 87
      {
        "name" : "ohos.permission.PERMISSION1",
        "reason": "$string:reason",
        "usedScene": {
88
          "ability": [
89 90 91
            "FormAbility"
          ],
          "when":"inuse"
92 93 94 95 96 97
        }
      },
      {
        "name" : "ohos.permission.PERMISSION2",
        "reason": "$string:reason",
        "usedScene": {
98
          "ability": [
99 100
            "FormAbility"
          ],
101
          "when":"always"
102 103
        }
      }
104
    ]
105
  }
106 107
}
```
F
update  
fanchenxuan 已提交
108

Z
zengyawen 已提交
109 110
### 权限使用理由的文案内容规范

Z
zengyawen 已提交
111
当申请的权限为user_grant权限时,字段reason(申请权限的原因)必填。开发者需要在应用配置文件中,配置每一个需要使用的权限。
Z
zengyawen 已提交
112

Z
zengyawen 已提交
113
但在实际向用户弹窗申请授权时,user_grant权限将会以[权限组](accesstoken-overview.md#权限组和子权限)向用户申请。当前支持的权限组请查看[应用权限组列表](permission-group-list.md)
Z
zengyawen 已提交
114

Z
zengyawen 已提交
115
**reason字段的内容写作规范及建议如下:**
Z
zengyawen 已提交
116

Z
zengyawen 已提交
117
1. 保持句子简洁、不要加入多余的分割符号。
Z
zengyawen 已提交
118 119 120 121 122

   **建议句式**:用于某事。

   **示例**:用于扫码拍照。

Z
zengyawen 已提交
123
2. 用途描述的字串建议小于72个字符(即36个中文字符,UI界面显示大约为两行)。不能超过256个字符,以免在多语言适配后体验不好。
Z
zengyawen 已提交
124

Z
zengyawen 已提交
125
3. 如果不写,将展示默认的申请理由。
Z
zengyawen 已提交
126 127 128

**权限使用理由展示方式:**

Z
zengyawen 已提交
129 130 131 132 133 134 135
权限使用理由有两个展示途径:授权弹窗界面和“设置(Settings)”界面。“设置”的具体路径:设置-隐私-权限管理-某应用某权限详情

1. 如果是申请“电话、信息、日历、通讯录、通话记录”这五个权限组中的权限,根据工信部要求,将展示具体子权限的内容与用途。

   **句式**:包括子权限A和子权限B,用于某事。

   **样例**:用于获取通话状态和移动网络信息,用于安全运营和统计计费服务。
Z
zengyawen 已提交
136

Z
zengyawen 已提交
137
2. 如果是申请其他权限组中的权限。系统将使用权限组内当前被申请的第一个子权限的使用理由,作为该权限组的使用理由进行展示。组内的排序,固定按照权限管理内排列的权限组数组顺序。
Z
zengyawen 已提交
138

Z
zengyawen 已提交
139
   举例说明:权限组A = {权限A, 权限B, 权限C};申请传入的是{权限C, 权限B},界面将展示权限B中的权限使用理由。
Z
zengyawen 已提交
140

zyjhandsome's avatar
zyjhandsome 已提交
141
## ACL方式声明
142

143
当应用需要申请`system_basic``system_core`等级的权限时,比应用默认权限等级`normal`更高。如果需要申请的权限等级高于应用默认的等级,需要使用ACL方式声明使用。
144

145
例如,如果应用需要访问用户公共目录中的音乐文件,需要申请`ohos.permission.WRITE_AUDIO`权限,该权限属于`system_basic`等级。如果应用需要截取屏幕图像,则需要申请`ohos.permission.CAPTURE_SCREEN`权限,该权限属于`system_core`等级。此时,需要将相关权限项配置到[HarmonyAppProvision配置文件](app-provision-structure.md)`acl`字段中。
146

147 148
```json
{
149 150 151
	// ...
	"acls":{
		"allowed-acls":[
152
			"ohos.permission.WRITE_AUDIO",
153
      "ohos.permission.CAPTURE_SCREEN"
154 155
		]
	}
156 157 158
}
```

159
## 向用户申请授权
L
l00520400 已提交
160

161
当应用需要访问用户的隐私信息或使用系统能力时,例如获取位置信息、访问日历、使用相机拍摄照片或录制视频等,应该向用户请求授权。这需要使用 `user_grant` 类型权限。在此之前,应用需要进行权限校验,以判断当前调用者是否具备所需的权限。如果权限校验结果表明当前应用尚未被授权该权限,则应使用动态弹框授权方式,为用户提供手动授权的入口。示意效果如下图所示。
162

Z
zengyawen 已提交
163 164
图1 向用户申请授权

165
![](figures/permission-read_calendar.png)
166

167 168 169
> **说明**:
>
> 每次访问受目标权限保护的接口之前,都需要使用 [requestPermissionsFromUser()](../reference/apis/js-apis-abilityAccessCtrl.md#requestpermissionsfromuser9) 接口请求相应的权限。用户可能在动态授予权限后通过系统设置来取消应用的权限,因此不能将之前授予的授权状态持久化。
170

171
### Stage模型
172

173 174
以允许应用读取日历信息为例进行说明。

175
1. 申请`ohos.permission.READ_CALENDAR`权限,配置方式请参见[配置文件权限声明](#配置文件权限声明)
176

177 178 179 180 181 182 183 184 185 186 187
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<abilityAccessCtrl.GrantStatus> {
     let atManager = abilityAccessCtrl.createAtManager();
     let grantStatus: abilityAccessCtrl.GrantStatus;
zyjhandsome's avatar
zyjhandsome 已提交
188
   
189 190 191 192 193 194 195
     // 获取应用程序的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) {
196
       console.error(`Failed to get bundle info for self. Code is ${err.code}, message is ${err.message}`);
197 198 199 200 201 202
     }
   
     // 校验应用是否被授予权限
     try {
       grantStatus = await atManager.checkAccessToken(tokenId, permission);
     } catch (err) {
203
       console.error(`Failed to check access token. Code is ${err.code}, message is ${err.message}`);
204
     }
zyjhandsome's avatar
zyjhandsome 已提交
205
   
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
     return grantStatus;
   }
   
   async function checkPermissions(): Promise<void> {
     const permissions: Array<Permissions> = ['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中向用户申请授权。

229 230
   ```typescript
   import UIAbility from '@ohos.app.ability.UIAbility';
231
   import window from '@ohos.window';
232
   import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';
233
   
234 235
   const permissions: Array<Permissions> = ['ohos.permission.READ_CALENDAR'];
   
236
   export default class EntryAbility extends UIAbility {
237
     // ...
238
   
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
     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<number> = data.authResults;
         let length: number = grantStatus.length;
         for (let i = 0; i < length; i++) {
           if (grantStatus[i] === 0) {
             // 用户授权,可以继续访问目标操作
           } else {
             // 用户拒绝授权,提示用户必须授权才能访问当前页面的功能,并引导用户到系统设置中打开相应的权限
             return;
           }
         }
         // 授权成功
       }).catch((err) => {
258
         console.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`);
259 260 261
   
       // ...
     }
262 263
   }
   ```
264 265

   在UI中向用户申请授权。
Z
zengyawen 已提交
266

267
   ```typescript
268
   import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';
269 270
   import common from '@ohos.app.ability.common';
   
271 272
   const permissions: Array<Permissions> = ['ohos.permission.READ_CALENDAR'];
   
273 274 275
   @Entry
   @Component
   struct Index {
276
     reqPermissionsFromUser(permissions: Array<Permissions>): void {
277
       let context = getContext(this) as common.UIAbilityContext;
278
       let atManager = abilityAccessCtrl.createAtManager();
279
       // requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗
280
       atManager.requestPermissionsFromUser(context, permissions).then((data) => {
281
         let grantStatus: Array<number> = data.authResults;
282 283
         let length: number = grantStatus.length;
         for (let i = 0; i < length; i++) {
284 285 286 287
           if (grantStatus[i] === 0) {
             // 用户授权,可以继续访问目标操作
           } else {
             // 用户拒绝授权,提示用户必须授权才能访问当前页面的功能,并引导用户到系统设置中打开相应的权限
288 289
             return;
           }
290
         }
291
         // 授权成功
292
       }).catch((err) => {
293
         console.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`);
294 295 296 297 298 299 300 301 302
       })
     }
   
     // 页面展示
     build() {
       // ...
     }
   }
   ```
303

304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
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) => {
       // ...
     })
   }
   ```

325
### FA模型
L
l00520400 已提交
326

327
通过调用[requestPermissionsFromUser()](../reference/apis/js-apis-inner-app-context.md#contextrequestpermissionsfromuser7)接口向用户动态申请授权。
L
l00520400 已提交
328 329

```js
F
fanchenxuan 已提交
330 331 332 333
import featureAbility from '@ohos.ability.featureAbility';

reqPermissions() {
    let context = featureAbility.getContext();
F
fanchenxuan 已提交
334 335
    let array:Array<string> = ["ohos.permission.PERMISSION2"];
    //requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗
F
fanchenxuan 已提交
336
    context.requestPermissionsFromUser(array, 1).then(function(data) {
337 338 339
        console.log("data:" + JSON.stringify(data));
        console.log("data permissions:" + JSON.stringify(data.permissions));
        console.log("data result:" + JSON.stringify(data.authResults));
F
fanchenxuan 已提交
340
    }, (err) => {
341
        console.error('Failed to start ability', err.code);
F
fanchenxuan 已提交
342
    });
343
}
L
l00520400 已提交
344
```
Z
zengyawen 已提交
345

346 347
## 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`
348

349 350
- `bundleName`字段配置为应用的Bundle名称。
- `app_signature`字段配置为应用的指纹信息。指纹信息的配置参见[应用特权配置指南](../../device-dev/subsystems/subsys-app-privilege-config-guide.md#install_list_capabilityjson中配置)
zyjhandsome's avatar
zyjhandsome 已提交
351
- `permissions`字段中`name`配置为需要预授权的`user_grant`类型的权限名;`permissions`字段中`userCancellable`表示为用户是否能够取消该预授权,配置为true,表示支持用户取消授权,为false则表示不支持用户取消授权。
352

Z
zengyawen 已提交
353
> **说明**:
Z
zengyawen 已提交
354
>
Z
zengyawen 已提交
355
> 当前仅支持预置应用配置该文件。
L
lsq 已提交
356 357 358

```json
[
359
  // ...
L
lsq 已提交
360
  {
361 362
    "bundleName": "com.example.myapplication", // Bundle名称
    "app_signature": ["****"], // 指纹信息
L
lsq 已提交
363 364
    "permissions":[
      {
365 366
        "name": "ohos.permission.PERMISSION_X", // user_grant类型预授权的权限名
        "userCancellable": false // 用户不可取消授权
L
lsq 已提交
367 368
      },
      {
369 370
        "name": "ohos.permission.PERMISSION_Y", // user_grant类型预授权的权限名
        "userCancellable": true // 用户可取消授权
L
lsq 已提交
371 372 373 374 375
      }
    ]
  }
]
```
376

377 378 379 380
## 相关实例

针对访问控制,有以下相关实例可供参考:

Z
zwx1094577 已提交
381
- [AbilityAccessCtrl:访问权限控制(ArkTS)(Full SDK)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SystemFeature/Security/AbilityAccessCtrl)