1 Star 0 Fork 5.1K

piggyguy_jdx / docs

forked from OpenHarmony / docs 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
accesstoken-guidelines.md 16.23 KB
一键复制 编辑 原始数据 按行查看 历史

访问控制授权申请指导

场景介绍

应用的APL(Ability Privilege Level)等级分为normalsystem_basicsystem_core三个等级,默认情况下,应用的APL等级都为normal等级。权限类型分为system_grantuser_grant两种类型。应用可申请的权限项参见应用权限列表

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

配置文件权限声明

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

说明

应用在申请system_basicsystem_core等级权限时,需要提升权限等级,因为应用默认的权限等级为normal。如果应用需要申请高于默认等级的权限,除了在配置文件中进行声明之外,还需要通过ACL方式进行声明使用。

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

标签 是否必填 说明
name 权限名称。
reason 描述申请权限的原因,可参考权限使用理由的文案内容规范
> 说明:当申请的权限为user_grant权限时,此字段必填。
usedScene 描述权限使用的场景和时机。
> 说明:当申请的权限为user_grant权限时,此字段必填。
abilities 标识需要使用到该权限的Ability,标签为数组形式。
适用模型:Stage模型
ability 标识需要使用到该权限的Ability,标签为数组形式。
适用模型:FA模型
when 标识权限使用的时机,值为inuse/always
- inuse:表示为仅允许前台使用。
- always:表示前后台都可使用。

Stage模型

使用Stage模型的应用,需要在module.json5配置文件中声明权限。

{
  "module" : {
    // ...
    "requestPermissions":[
      {
        "name" : "ohos.permission.PERMISSION1",
        "reason": "$string:reason",
        "usedScene": {
          "abilities": [
            "FormAbility"
          ],
          "when":"inuse"
        }
      },
      {
        "name" : "ohos.permission.PERMISSION2",
        "reason": "$string:reason",
        "usedScene": {
          "abilities": [
            "FormAbility"
          ],
          "when":"always"
        }
      }
    ]
  }
}

FA模型

使用FA模型的应用,需要在config.json配置文件中声明权限。

{
  "module" : {
    // ...
    "reqPermissions":[
      {
        "name" : "ohos.permission.PERMISSION1",
        "reason": "$string:reason",
        "usedScene": {
          "ability": [
            "FormAbility"
          ],
          "when":"inuse"
        }
      },
      {
        "name" : "ohos.permission.PERMISSION2",
        "reason": "$string:reason",
        "usedScene": {
          "ability": [
            "FormAbility"
          ],
          "when":"always"
        }
      }
    ]
  }
}

权限使用理由的文案内容规范

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

但在实际向用户弹窗申请授权时,user_grant权限将会以权限组向用户申请。当前支持的权限组请查看应用权限组列表

reason字段的内容写作规范及建议如下:

  1. 保持句子简洁、不要加入多余的分割符号。

    建议句式:用于某事。

    示例:用于扫码拍照。

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

  3. 如果不写,将展示默认的申请理由。

权限使用理由展示方式:

权限使用理由有两个展示途径:授权弹窗界面和“设置(Settings)”界面。“设置”的具体路径:设置-隐私-权限管理-某应用某权限详情

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

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

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

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

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

ACL方式声明

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

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

{
	// ...
	"acls":{
		"allowed-acls":[
			"ohos.permission.WRITE_AUDIO",
      "ohos.permission.CAPTURE_SCREEN"
		]
	}
}

向用户申请授权

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

图1 向用户申请授权

说明

每次访问受目标权限保护的接口之前,都需要使用 requestPermissionsFromUser() 接口请求相应的权限。用户可能在动态授予权限后通过系统设置来取消应用的权限,因此不能将之前授予的授权状态持久化。

Stage模型

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

  1. 申请ohos.permission.READ_CALENDAR权限,配置方式请参见配置文件权限声明

  2. 校验当前是否已经授权。

    在进行权限申请之前,需要先检查当前应用程序是否已经被授予了权限。可以通过调用checkAccessToken()方法来校验当前是否已经授权。如果已经授权,则可以直接访问目标操作,否则需要进行下一步操作,即向用户申请授权。

    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;
    
      // 获取应用程序的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(`Failed to get bundle info for self. Code is ${err.code}, message is ${err.message}`);
      }
    
      // 校验应用是否被授予权限
      try {
        grantStatus = await atManager.checkAccessToken(tokenId, permission);
      } catch (err) {
        console.error(`Failed to check access token. Code is ${err.code}, message is ${err.message}`);
      }
    
      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()方法来实现。该方法接收一个权限列表参数,例如位置、日历、相机、麦克风等。用户可以选择授予权限或者拒绝授权。

    可以在UIAbility的onWindowStageCreate()回调中调用requestPermissionsFromUser()方法来动态申请权限,也可以根据业务需要在UI中向用户申请授权。

    在UIAbility中向用户申请授权。

    import UIAbility from '@ohos.app.ability.UIAbility';
    import window from '@ohos.window';
    import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';
    
    const permissions: Array<Permissions> = ['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<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(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`);
        })
        // ...
      }
    }

    在UI中向用户申请授权。

    import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';
    import common from '@ohos.app.ability.common';
    
    const permissions: Array<Permissions> = ['ohos.permission.READ_CALENDAR'];
    
    @Entry
    @Component
    struct Index {
      reqPermissionsFromUser(permissions: Array<Permissions>): void {
        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(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`);
        })
      }
    
      // 页面展示
      build() {
        // ...
      }
    }
  4. 处理授权结果。

    调用requestPermissionsFromUser()方法后,应用程序将等待用户授权的结果。如果用户授权,则可以继续访问目标操作。如果用户拒绝授权,则需要提示用户必须授权才能访问当前页面的功能,并引导用户到系统设置中打开相应的权限。

    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模型

通过调用requestPermissionsFromUser()接口向用户动态申请授权。

import featureAbility from '@ohos.ability.featureAbility';

reqPermissions() {
    let context = featureAbility.getContext();
    let array:Array<string> = ["ohos.permission.PERMISSION2"];
    //requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗
    context.requestPermissionsFromUser(array, 1).then(function(data) {
        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);
    });
}

user_grant权限预授权

user_grant权限可以通过预授权方式请求权限。预授权方式需要预置配置文件,预置配置文件在设备上的路径为/system/etc/app/install_list_permission.json,设备开机启动时会读取该配置文件,在应用安装会对在文件中配置的user_grant类型权限授权。预授权配置文件字段内容包括bundleNameapp_signaturepermissions

  • bundleName字段配置为应用的Bundle名称。
  • app_signature字段配置为应用的指纹信息。指纹信息的配置参见应用特权配置指南
  • permissions字段中name配置为需要预授权的user_grant类型的权限名;permissions字段中userCancellable表示为用户是否能够取消该预授权,配置为true,表示支持用户取消授权,为false则表示不支持用户取消授权。
[
  // ...
  {
    "bundleName": "com.example.myapplication", // Bundle名称
    "app_signature": ["****"], // 指纹信息
    "permissions":[
      {
        "name": "ohos.permission.PERMISSION_X", // user_grant类型预授权的权限名
        "userCancellable": false // 用户不可取消授权
      },
      {
        "name": "ohos.permission.PERMISSION_Y", // user_grant类型预授权的权限名
        "userCancellable": true // 用户可取消授权
      }
    ]
  }
]

相关实例

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

1
https://gitee.com/piggyguy/docs.git
git@gitee.com:piggyguy/docs.git
piggyguy
docs
docs
master

搜索帮助