目 录CONTENT

文章目录

鸿蒙应用权限申请机制和实例

克林空间
2024-03-03 / 0 评论 / 1 点赞 / 265 阅读 / 0 字 / 正在检测是否收录...
温馨提示:
本文最后更新于2024-03-03,若内容或图片失效,请留言反馈。 部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

鸿蒙因为加强了系统安全,有了一套比较系统化的设计,所以在应用权限申请上涉及了挺多概念,看官方文档内容多容易迷糊。于是本文把主线流程精简出来,补充上一些支撑技术原理,也许能帮助提高些理解效率,常用的部分总体来说还是比较简单的,看完这篇应该就会用了。

ohos权限控制的整体框架ATM

说到OpenHarmonyOS的应用访问控制就必须先说ATM。这个ATM可不是提款机,根据官方说法,ATM(Access Token Manager)是OpenHarmonyOS上基于AccessToken构建的统一的应用权限管理能力。ATM通过对应用进行分级,执行最小授权原则,避免了权限滥用问题;同时支持跨平台执行权限访问控制功能,以便满足分布式应用场景。

应用的Accesstoken信息主要包括应用身份标识APPID、用户ID,应用分身索引、应用APL(Ability Privilege Level)等级、应用权限信息等。每个应用的Accestoken信息由一个32bits的设备内唯一标识符TokenID(Token identity)来标识。对于运行在设备上的应用, ATM为每个应用分配唯一的标识(TokenID),在资源使用时通过TokenID作为唯一身份标识映射获取对应应用的权限和授权状态信息,并依此进行鉴权,以管控应用的资源访问行为。拿腾讯视频来打比方,TokenID就类似腾讯视频的用户ID。

ATM模块主要提供如下功能:

  • 提供基于TokenID的应用权限校验机制,应用访问敏感数据或者API时可以检查是否有对应的权限。
  • 提供基于TokenID的Accestoken信息查询,应用可以根据TokenID查询自身的APL等级等信息。

应用在安装后首次启动时,如果遇到需要访问特定资源时,如:相册、摄像头等,会需要通过TokenID申请授权才能使用。

ATM部件的架构图如下所示:

鸿蒙应用权限申请-1

ATM总体管理服务作为ATM 服务的总体入口,它可以将业务请求分发到下级的三个模块:TokenID管理、权限管理和APL管理。

TokenID管理模块:在TokenID与AT信息一一对应的基础上,TokenID管理提供了TokenID及其对应AT信息的初始化创建、查询、更新以及删除等服务。当目标应用被拉起时,ATM会为其分配唯一身份标识TokenID,并保存应用的初始化AT信息。。

● TokenID是每个应用的身份标识(可以理解为应用的身份证)。

● AT信息包括应用身份标识APP ID、子用户ID、应用分身索引信息、应用APL、应用权限授权状态等信息(可以理解为应用的身份证信息)。

权限管理模块:主要提供应用权限定义信息、应用权限授权状态信息的处理服务。权限管理模块在TokenID管理模块的基础上,向业务提供应用的权限信息查询、授权、鉴权等服务,管理应用权限的使用记录,构筑ATM的应用权限访问控制功能。

APL管理模块:APL管理模块基于唯一身份标识TokenID,提供应用的权限申请合法性校验功能,规范化权限申请范围,进行权限最小化管理。当前,应用的权限申请规则是基于洋葱式访问控制模型的,下面请跟随小编继续了解洋葱式访问控制模型。

对于应用来说,在ATM框架中主要是3个相关场景:

  1. 在应用安装时,需要调用AllocHapToken创建获取该应用的TokenID。
  2. 在应用运行过程中,需要进行鉴权等操作时,可调用VerifyAccessToken、GetReqPermissions等函数查询校验应用权限、APL等信息。
  3. 在应用卸载时,需要调用DeleteToken函数删除系统中管理的对应Accesstoken信息。

对于普通的应用开发来说,主要使用第2项里的接口,第1项和第3项由系统应用管理自动完成。而ATM提供的其它应用层功能,则根据需要视情况而用。

ohos的权限

OpenHarmonyOS的访问控制中,一方是访问者,即TokenID,另一方是被访问者,即权限。访问控制本质就是对这两者之间的访问关系进行管理。ohos将系统的各类可访问资源进行权限定义,以API10为例,提供了229项应用权限 。这个还是拿腾讯视频来打比方的话,权限就是视频资源。权限的定义示例如下图:

鸿蒙应用权限申请机制和实例

权限级别

权限级别即元能力权限等级APL (Ability Privilege Level),指的是应用的权限申请优先级的定义,不同APL等级的应用能够申请的权限等级不同。大白话就是应用也是本等级的,高等级的就是VVIP,默认就可以访问更核心的资源。应用的等级可以分为三个等级,如下表:

APL级别说明类比腾讯视频
system_core等级该等级的应用服务提供操作系统核心能力。超级影视SVIP
system_basic等级该等级的应用服务提供系统基础服务。腾讯视频VIP
normal等级普通应用。普通会员

上面说的三个等级也正是基于洋葱式访问控制模型的设计,洋葱式访问控制模型分为三层,从里往外看:最里层是操作系统核心层,应用的APL=system_core。中间层是系统增强服务层,应用的APL=system_basic。最外层则是普通应用程序层,应用的APL=system_normal。

鸿蒙应用权限申请机制和实例-2

不同APL等级的应用能申请的权限范围也是不同的,下面一起看一下APL等级的划分规则。

A. 操作系统核心能力APL=“system_core”

操作系统核心能力是系统最核心的底层服务,它需要拥有所有权限以便实现对系统的管理。

操作系统核心能力包括:AMS(Ability Manager Service,能力管理系统)、BMS(Bundle Manager Service,包管理系统)、DMS(Distributed Manager Service,分布式消息系统)、软总线等。

操作系统核心能力的APL=“system_core”。这类应用可申请访问操作系统核心资源的权限,对系统的影响程度非常大,目前只对系统服务开放。

B. 系统基础服务APL=“system_basic”

在操作系统核心能力基础上,为操作系统提供基础服务的应用就叫系统基础服务,系统基础服务包括:

➢ 最小集基础应用,提供用户进行设备操作时所必需的最小集基础应用。如系统启动、系统设置、身份认证、系统调度和管理等。

➢ 智慧化服务,提供智慧化基础服务。如AR、VR、AI引擎的服务。

➢ 系统调度和管理应用,提供系统最基本的性能、功耗、后台应用的管理功能。

系统基础服务的APL=“system_basic”,这类应用可申请访问操作系统基础服务相关资源的权限。

C. 普通应用APL=“normal”

普通应用包括三方应用和不在系统基础服务范围内的预置应用。三方应用包括社交类、资讯类、视频播放类、游戏类等应用。预置应用包括时钟、天气等应用。

普通应用的APL=“normal”。这类应用是操作系统非必要软件,通过开放接口即可实现此类应用的功能。

注:若应用想要提升自身的APL等级,需要通过应用市场的审核。

洋葱式访问控制模型通过对应用实行严格的等级制度管控,根据不同的等级制定不同的安全和访问控制策略,实现了权限范围可控目标。同时为应用在跨设备运行时提供统一的隐私保护机制。

默认情况下,应用的APL等级都为normal等级。如果应用需要将自身的APL等级声明为system_basic及以上的APL等级,在开发应用安装包时,要修改应用的Profile文件。不过直接修改应用Profile文件的方式,仅用于应用/服务调试阶段,不可用于发布上架应用市场。如果需要开发商用版本的应用,要在对应的应用市场进行发布证书和Profile文件的申请。

授权方式

权限类型可分为system_grant(系统授权)和user_grant(用户授权)。

  • system_grant

    system_grant指的是系统授权类型,在该类型的权限许可下,应用被允许访问的数据不会涉及到用户或设备的敏感信息,应用被允许执行的操作不会对系统或者其他应用产生大的不利影响。

    如果在应用中申请了system_grant权限,那么系统会在用户安装应用时,自动把相应权限授予给应用。应用需要在应用商店的详情页面,向用户展示所申请的system_grant权限列表。相当于安装前告知,如果不允许则不要安装,安装即允许。

  • user_grant

    user_grant指的是由用户动态授权,在该类型的权限许可下,应用被允许访问的数据将会涉及到用户或设备的敏感信息,应用被允许执行的操作可能对系统或者其他应用产生严重的影响。

    该类型权限不仅需要在安装包中申请权限,还需要在应用动态运行时,通过发送弹窗的方式请求用户授权。在用户手动允许授权后,应用才会真正获取相应权限,从而成功访问操作目标对象。

    比如说,在权限定义列表中,麦克风和摄像头对应的权限都是属于用户授权权限,列表中给出了详细的权限使用理由。

    应用需要在应用商店的详情页面,向用户展示所申请的user_grant权限列表。

如果应用需要获取目标权限,那么需要先进行权限申请。申请步骤如下:

  • 权限声明

    开发者需要在配置文件中声明目标权限。

  • 权限授权

    • 如果目标权限是system_grant类型,开发者在进行权限申请后,系统会在安装应用时自动为其进行权限预授予,开发者不需要做其他操作即可使用权限。
    • 如果目标权限是user_grant类型,开发者在进行权限申请后,在运行时触发动态弹窗,请求用户授权。

ACL使能

ACL即访问控制列表(Access Control List),提供了解决低等级应用访问高等级权限问题的特殊渠道。类似于腾讯视频里的影片单独付费功能,即虽然你不是VIP,但如果你为这部影片单独支持10元钱,也是可以看滴。

如前面APL定义的,权限和APL有一一对应关系。原则上,低APL等级的应用默认无法使用更高等级的权限。这时候如果需要使用更高的权限,就需要看这个权限是否支持ACL使能。即通过特殊的声明,获取访问该权限的能力。

ACL使能有两种使用方法:

一种用于应用或服务开发调试阶段使用。直接修改SDK目录里的OpenHarmony SDK目录/Toolchains/{Version}/lib>UnsgnedReleasedProfileTemplate.json 文件。这里也有两种方式修改。一种是直接修改apl配置项等级,把应用定义为更高权限级别的应用,就可以直接获取高权限的资源。第二种是修改文件中的acls配置,把对应的权限名称填充到allowed-acls数组里,通过这个声明,就可以获取访问资格。

另一种用于商用版本应用,需要在应用市场进行发布证书和Profile文件的申请。

起始版本

这个很好理解,表示从哪个API版本开始提供。比如:10则就是从支持API10的系统开始,都可以使用这个权限定义。

实例

上面的机制在实际应用开发中可以串成一个检查流程,重新整理了一个流程图如下:
鸿蒙权限申请流程 1

拿腾讯视频的例子帮助理解就很简单:首先看你要访问什么资源,如果是普通视频,那就放广告后直接放行。如果是会员才能看的电影,那就要先看看你是什么身份的用户(normal、system_basic、system_core?)。如果你是超级VIP那就体贴的加上一段VIP专属的广告后才放行,如果你是普通会员就只让你看5分钟,当然广告还是不能忘了。如果你不想充会员,又想看这电影,也还是有办法的。如果这电影支持单独付费(ACL使能),那就看你付不付(配置允许ACL项),付了也放行。不付的话,哼哼……。当然重要的事情最终都需要用户确认服务协议 (user_grant),要尊重用户。有的小变化不需要麻烦用户,自己处理下就行(system_grant)。

实操需求:假如某即将改变世界的应用要获取设备的udid信息和日历信息,经过在权限列表中查询,需要申请如下两个权限。

  1. 日历读权限

鸿蒙应用权限申请机制和实例

  1. UDID访问权限

鸿蒙应用权限申请机制和实例-1

根据上面的流程可知,我们在应用开发中要做这样几件事:

1.声明应用所需权限

日历的读权限普通会员即可申请,但要用户授权,那么声明权限只需要在当前模块下的module.json5文件中加上配置项即可。

UDID信息权限需要system_basic的APL级别才能访问,我们的应用通常只是normal级别,所以需要看是否有ACL使能,由权限定义可知这个权限支持ACL使能,所以可以修改 OpenHarmony SDK目录/Toolchains/{Version}/lib>UnsgnedReleasedProfileTemplate.json 申请权限。

在module.json5文件的abilities元素同级增加requestPermissions配置,对于需要用户在前台使用的权限需要在abilities里配置上对应的ability名称:

"requestPermissions": [  
  {  
    "name": "ohos.permission.sec.ACCESS_UDID",  
    "reason": "$string:reason",  
    "usedScene": {  
      "abilities": [  
        "EntryAbility"  
      ],  
      "when": "always"  
    }  
  },  
  {  
    "name": "ohos.permission.READ_CALENDAR",  
    "reason": "$string:reason",  
    "usedScene": {  
      "abilities": [  
        "EntryAbility"  
      ],  
      "when": "inuse"  
    }  
  }  
]

应用需要在项目的module.json5配置文件中逐个声明所需的权限,否则应用将无法获取授权。不管是什么授权方式,system_grant(系统授权)还是user_grant(用户授权)都需要在配置文件中声明。

在配置文件的requestPermissions属性数组中配置所需的权限,包含3个属性:name、reason和usedScene。

其中:

  • name 指定权限名称,是必填项。
  • reason 描述申请权限的原因,可选项。
  • usedScene 描述权限使用的场景和时机,可选项。
    • abilities:标识需要使用到该权限的Ability,标签为数组形式。可选项。
    • when:标识权限使用的时机,值为inuse/always。inuse:表示为仅允许前台使用;always:表示前后台都可使用。

在UnsgnedReleasedProfileTemplate.json中的acls增加以下内容:

	"acls":{
		"allowed-acls":[
			"ohos.permission.sec.ACCESS_UDID"
		]
	}

当然,你也可以直接提高应用APL等级,就不用一个个改acls了:

"apl":"system_basic",

2.申请用户动态授权

对于日历信息访问权限,由于它的授权方式是user_grant,所以必须以动态弹框的方式向用户申请授权。

在进行权限申请之前,需要先检查当前应用程序是否已经被授予了权限。可以调用checkAccessToken()方法。如果已经授权,则可以直接访问目标操作,否则需要向用户申请授权。这部分可以直接使用openharmonyos在线文档的样例代码。

import bundleManager from '@ohos.bundle.bundleManager';
import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';
import { BusinessError } from '@ohos.base';

async function checkAccessToken(permission: Permissions): Promise<abilityAccessCtrl.GrantStatus> {
  let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
  let grantStatus: abilityAccessCtrl.GrantStatus = abilityAccessCtrl.GrantStatus.PERMISSION_DENIED;

  // 获取应用程序的accessTokenID
  let tokenId: number = 0;
  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 (error) {
    let err: BusinessError = error as BusinessError;
    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 (error) {
    let err: BusinessError = error as BusinessError;
    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 {
    // 申请日历权限
  }
}

接上述代码,如果没授权,则需要动态向用户申请权限。动态向用户申请权限是指在应用程序运行时向用户请求授权的过程。可以通过调用requestPermissionsFromUser()方法来实现。该方法接收一个权限列表参数,例如位置、日历、相机、麦克风等。用户可以选择授予权限或者拒绝授权。

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

这里以我们常用的UI为例,也可以直接查看在线文档

import abilityAccessCtrl, { Context, PermissionRequestResult, Permissions } from '@ohos.abilityAccessCtrl';
import { BusinessError } from '@ohos.base';

const permissions: Array<Permissions> = ['ohos.permission.READ_CALENDAR'];

@Entry
@Component
struct Index {
 reqPermissionsFromUser(permissions: Array<Permissions>): void {
   let context: Context = getContext(this) as common.UIAbilityContext;
   let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
   // requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗
   atManager.requestPermissionsFromUser(context, permissions).then((data: PermissionRequestResult) => {
     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: BusinessError) => {
     console.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`);
   })
 }

 // 页面展示
 build() {
   // ...
 }
}

完成以上配置声明和动态授权两部分内容,则两个权限都可以使用了,不需要user_grant的权限只需要第1步声明权限就可以了。

参考资料:
访问控制(权限)开发概述 (openharmony.cn)
访问控制授权申请指导 (openharmony.cn)
应用权限列表 (openharmony.cn)
一文带你看懂ATM的应用权限访问控制能力_华为_科技观察-华为云开发者联盟 (csdn.net)

1

评论区