开发者技术支持-鸿蒙应用权限申请分析与最佳实践

举报
yd_242815780 发表于 2026/03/09 18:33:53 2026/03/09
【摘要】 1.1 问题说明在鸿蒙原生应用开发中,权限管理是确保应用功能正常运作的关键环节。使用 Normal 级别证书开发的应用,其可申请的权限分为系统授权权限和用户授权权限两类。开发者在配置和使用权限时,若不区分这两类权限的配置要求和申请流程,可能导致权限配置错误、功能无法正常使用,甚至应用闪退。本文将详细分析两类权限的区别、配置规范及配套的权限检查与申请代码实现。1.2 原因分析• 权限分类机制鸿...


1.1 问题说明

在鸿蒙原生应用开发中,权限管理是确保应用功能正常运作的关键环节。使用 Normal 级别证书开发的应用,其可申请的权限分为系统授权权限和用户授权权限两类。开发者在配置和使用权限时,若不区分这两类权限的配置要求和申请流程,可能导致权限配置错误、功能无法正常使用,甚至应用闪退。本文将详细分析两类权限的区别、配置规范及配套的权限检查与申请代码实现。

1.2 原因分析

权限分类机制

鸿蒙系统将权限分为 system_grant(系统授权)和 user_grant(用户授权)两类:

1.系统授权权限:安装时自动授予,如网络权限(ohos.permission.INTERNET)

2.用户授权权限:运行时需动态申请,如定位权限(ohos.permission.LOCATION)

配置差异导致的错误

两类权限在module.jsons中的配置要求不同:

1.系统授权权限:仅需申明name字段

2.用户授权权限:必须完整配置reason和usedscene,否则无法通过应用上架审核或运行时授权失败

权限检查缺失风险

直接调用涉及敏感权限的 API 前未进行权限状态检查,会导致:

1.权限未授予时调用 API 引发安全异常

2.用户拒绝授权后无降级处理逻辑,功能不可用且无提示

动态申请流程不完整

用户授权权限需要先检查状态,未授权时需主动调用 `requestPermissionsFromUser` 触发系统弹窗,缺失此环节将导致权限永远无法获取。

1.3 解决思路

明确权限分类

1.查阅官方文档确认目标权限属于 system_grant 还是 user_grant

2.根据分类在 `module.json5` 中进行差异配置

封装统一的权限检查工具

1.创建可复用的权限状态检查函数

2.支持同步/异步获取当前授权状态

实现权限申请流程标准化

1.封装权限请求函数,处理用户授权弹窗逻辑

2.统一处理授权成功/失败的回调与错误提示

建立权限使用规范

1.调用敏感 API 前必须经过权限检查

2.用户拒绝时引导至系统设置开启权限

数据量监控与预校验

开发阶段通过 JSON.stringify(data).length 检查数据大小,提前发现超限风险。

1.4 解决方案

a.module.json5 权限配置规范

{

"requestPermissions": [

// 用户授权权限示例(需完整配置)

{

"name": "ohos.permission.LOCATION",

"reason": "$string:permissionsReason", // 必填:使用权限的理由

"usedScene": { // 必填:使用场景

"abilities": [

"EntryAbility"

],

"when": "always" // 调用时机:always/inuse

}

},

{

"name": "ohos.permission.APPROXIMATELY_LOCATION",

"reason": "$string:permissionsReason",

"usedScene": {

"abilities": [

"EntryAbility"

],

"when": "always"

}

},

// 系统授权权限示例(仅需name)

{

"name": "ohos.permission.INTERNET" // 系统权限,安装时自动授予

}

]

}

b.权限状态检查工具函数

import { abilityAccessCtrl, bundleManager, Permissions } from '@kit.AbilityKit';

import { BusinessError } from '@kit.BasicServicesKit';




/**

* 检查单个权限的授予状态

* @param permission 权限名称

* @returns 授权状态 GrantStatus

*/

export async function checkPermissionGrant(permission: Permissions): Promise<abilityAccessCtrl.GrantStatus> {

// 获取应用程序的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) {

const err: BusinessError = error as BusinessError;

console.error(`Failed to get bundle info: ${err.code}, ${err.message}`);

}




// 校验应用是否被授予权限

try {

let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();

let grantStatus: abilityAccessCtrl.GrantStatus = await atManager.checkAccessToken(tokenId, permission);

return grantStatus;

} catch (error) {

const err: BusinessError = error as BusinessError;

console.error(`Failed to check access token: ${err.code}, ${err.message}`);

return abilityAccessCtrl.GrantStatus.PERMISSION_DENIED;

}

}




/**

* 检查权限是否已授予(布尔值版本)

* @param permissions 权限名称

* @returns true=已授权,false=未授权

*/

export async function checkPermissions(permissions: Permissions): Promise<boolean> {

let grantStatus: abilityAccessCtrl.GrantStatus = await checkPermissionGrant(permissions);

return grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;

}

c. 用户授权权限动态申请

import { common, abilityAccessCtrl, Permissions } from '@kit.AbilityKit';

import { BusinessError } from '@kit.BasicServicesKit';




/**

* 向用户申请权限(弹窗请求)

* @param permissions 需要申请的权限数组

* @param context UIAbility上下文

* @returns Promise<number> 0=全部授权成功,其他=失败

*/

export function reqPermissionsFromUser(

permissions: Array<Permissions>,

context: common.UIAbilityContext

): Promise<number> {

return new Promise(async (resolve, reject) => {

let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();




try {

// requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗

const data = await atManager.requestPermissionsFromUser(context, permissions);

console.info(`requestPermissionsFromUser result: ${JSON.stringify(data)}`);




let grantStatus: Array<number> = data.authResults;




// 检查所有权限是否都授予

for (let i = 0; i < grantStatus.length; i++) {

if (grantStatus[i] !== 0) { // 0 表示授予

// 用户拒绝授权,引导至系统设置

console.warn(`Permission ${permissions[i]} denied`);

reject(new Error('用户拒绝授权,请在系统设置中手动开启权限'));

return;

}

}




// 所有权限都授予成功

resolve(0);




} catch (error) {

const err: BusinessError = error as BusinessError;

console.error(`Failed to request permissions: ${err.code}, ${err.message}`);

reject(err);

}

});

}

d. 完整的使用示例(以定位权限为例)

import { Permissions } from '@kit.AbilityKit';




// 定义需要检查的权限

const LOCATION_PERMISSIONS: Permissions[] = [

'ohos.permission.LOCATION',

'ohos.permission.APPROXIMATELY_LOCATION'

];




/**

* 定位功能权限处理完整流程

*/

async function handleLocationPermission(context: common.UIAbilityContext): Promise<boolean> {

try {

// 1. 逐个检查权限状态

for (const perm of LOCATION_PERMISSIONS) {

const isGranted = await checkPermissions(perm);

if (!isGranted) {

console.info(`Permission ${perm} not granted, requesting...`);




// 2. 未授权则发起申请

await reqPermissionsFromUser(LOCATION_PERMISSIONS, context);




// 3. 申请后重新确认状态

for (const checkPerm of LOCATION_PERMISSIONS) {

const finalStatus = await checkPermissions(checkPerm);

if (!finalStatus) {

console.error(`Permission ${checkPerm} still denied after request`);

return false;

}

}




break; // 权限已成功申请

}

}




console.info('All location permissions granted');

return true;




} catch (error) {

console.error('Permission request failed:', error);

// 可在此处展示引导弹窗,提示用户手动开启权限

return false;

}

}

1.5 总结

问题说明:鸿蒙应用中 Normal 级别证书的权限分为 system_grant(系统授权)和 user_grant(用户授权)两类,二者在配置声明、申请流程和用户交互方面存在显著差异。错误配置或流程缺失会导致功能异常、运行崩溃或上架失败。

痛点总结:

o 配置混淆:混淆两类权限的配置要求,用户权限缺少 reason/usedScene 导致审核不通过

o 检查缺失:调用敏感 API 前未验证权限状态,引发安全异常

o 申请遗漏:用户权限未触发动态申请弹窗,导致权限永久缺失

o 体验断层:用户拒绝后无引导措施,功能不可用且无反馈

技术总结:

o 配置规范system_grant 仅需 name,user_grant 必须完整填写 reason 和 usedScene

o 检查工具:封装 `checkPermissions` 函数统一处理权限状态查询

o 申请流程:通过 `requestPermissionsFromUser` 触发系统授权弹窗,异步处理结果

o 降级策略:用户拒绝时提供日志提示,并引导至系统设置手动授权

避坑建议:

o 权限声明必填项:开发阶段对照官方文档确认每个权限的分类

o 调用前必检查:任何涉及 user_grant 权限的 API 调用前,务必先调用 `checkPermissions`

o 申请失败有引导:用户拒绝后,在 UI 层面给出友好提示,并提供跳转系统设置的能力

o 复用工具函数:将权限检查与申请逻辑封装为通用模块,避免重复代码

o 关注 SDK 版本:不同 API 版本对权限的处理可能有差异,建议使用稳定 API

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。