第4.2篇:安全权限管理------abilityAccessCtrl 实战
难度 :⭐⭐ 进阶
前置知识 :第 4.1 篇 canIUse 系统能力检测
涉及源文件 :
products/default/src/main/ets/services/PermissionGuard.ets、products/default/src/main/ets/components/CreationComponents.ets

概述
HarmonyOS 的权限管理体系与 Android 和 iOS 既有相似之处,也有独特的设计理念。在"画伴梦工厂"中,我们需要使用相机拍照、调用麦克风录音、从相册选择图片------这些操作都涉及敏感权限的申请与管理。
本文将通过项目中的 PermissionGuard 服务,系统讲解 HarmonyOS 权限管理的核心 API abilityAccessCtrl,涵盖权限请求的异步流程、结果回调处理、敏感权限的申请策略以及 Scope 作用域访问的实践。
一、HarmonyOS 权限体系概览
1.1 权限分类
HarmonyOS 将权限分为三个层级:
| 权限等级 | 说明 | 示例 | 申请方式 |
|---|---|---|---|
| system_basic(基础) | 对系统基本资源的访问 | ohos.permission.INTERNET |
仅需 module.json5 声明 |
| system_core(核心) | 对用户敏感数据的访问 | ohos.permission.CAMERA、ohos.permission.MICROPHONE |
须动态弹窗授权 |
| system_basic(受限) | 存在隐私风险的普通权限 | ohos.permission.READ_MEDIA |
部分设备安装时拒绝 |
其中,CAMERA 和 MICROPHONE 属于 system_core 级别 的敏感权限,必须通过 abilityAccessCtrl 的动态授权 API 在运行时弹窗询问用户。
1.2 权限申请流程
在 HarmonyOS 中,一个完整的权限申请流程包含三步:
- 静态声明 :在
module.json5中声明所需权限 - 动态申请 :在运行时调用
requestPermissionsFromUser弹窗询问 - 结果处理:根据用户的授权结果决定是否继续操作
只有第 1 步是编译期必需的,但实际项目中,核心敏感权限(如 CAMERA)应该在运行时动态申请,即使用户拒绝也不会导致应用崩溃。
二、PermissionGuard 服务封装
项目中将所有权限申请逻辑抽取为独立的 PermissionGuard 服务,放在 services/PermissionGuard.ets 中。这样做的好处是:权限逻辑与 UI 层解耦,便于维护和测试。
2.1 返回值类型定义
typescript
export interface PermissionResult {
granted: boolean; // 是否获得授权
message: string; // 授权失败时的提示信息
}
PermissionResult 是权限申请的通用接口,调用方通过检查 granted 字段决定后续逻辑。
2.2 统一请求入口
typescript
private static async request(context: common.UIAbilityContext,
permissions: Permissions[], deniedMessage: string): Promise<PermissionResult> {
try {
const manager = abilityAccessCtrl.createAtManager();
const result = await manager.requestPermissionsFromUser(context, permissions);
for (let i = 0; i < result.authResults.length; i++) {
if (result.authResults[i] !== 0) {
return { granted: false, message: deniedMessage };
}
}
return { granted: true, message: '' };
} catch (error) {
return { granted: false, message: deniedMessage };
}
}
三个方法的设计体现了清晰的职责分离:
request(私有)------通用的权限请求方法,接收权限列表和拒绝提示信息requestCamera------指定CAMERA权限和相机权限的提示文案requestMicrophone------指定MICROPHONE权限和麦克风权限的提示文案
三、abilityAccessCtrl.createAtManager() API 详解
abilityAccessCtrl 是 HarmonyOS 权限管理的核心模块,位于 @kit.AbilityKit 中。它提供了以下关键能力:
| API | 说明 |
|---|---|
createAtManager() |
创建 AccessToken(权限令牌)管理器实例 |
requestPermissionsFromUser() |
向用户弹窗请求权限,异步返回授权结果 |
verifyAccessToken() |
校验指定权限是否已被授予(同步检查) |
grantPermissions() |
系统级授予权限(仅供系统应用使用) |
revokePermissions() |
系统级撤销权限(仅供系统应用使用) |
在"画伴梦工厂"中,我们主要使用前两个 API:
typescript
import { abilityAccessCtrl, common, Permissions } from '@kit.AbilityKit';
const manager = abilityAccessCtrl.createAtManager();
设计要点:
createAtManager()是静态工厂方法,返回的是AtManager实例AtManager是单例模式------多次调用返回同一个实例- 每个应用进程只有一个
AtManager,因此可以在应用启动时创建一次并复用
四、requestPermissionsFromUser 异步流程
4.1 方法签名
typescript
requestPermissionsFromUser(
context: common.UIAbilityContext,
permissions: Permissions[]
): Promise<PermissionRequestResult>
参数说明:
| 参数 | 类型 | 说明 |
|---|---|---|
context |
common.UIAbilityContext |
当前 UIAbility 的上下文,用于弹出系统授权对话框 |
permissions |
Permissions[] |
要申请的权限数组,一次可申请多个权限 |
返回值 PermissionRequestResult 的结构:
typescript
interface PermissionRequestResult {
permissions: Permissions[]; // 申请的权限列表(与入参一致)
authResults: number[]; // 每个权限的授权结果,0 = 授权,-1 = 拒绝
}
4.2 异步调用模式
方法返回 Promise,因此可以使用 async/await 语法进行流程控制:
typescript
const result = await manager.requestPermissionsFromUser(context, permissions);
这行代码会:
- 弹出系统的权限请求对话框
- 阻塞等待用户做出选择(授权或拒绝)
- 用户操作完成后,Promise resolve 返回授权结果
4.3 context 参数获取
在 UI 组件中,context 通过 getContext(this) 获取:
typescript
const context = getContext(this) as common.UIAbilityContext;
getContext(this) 是 ArkUI 提供的内置函数,返回当前组件的上下文对象。需要强制转换为 common.UIAbilityContext 类型,因为 requestPermissionsFromUser 要求 UIAbilityContext 类型的参数。
五、PermissionResult 回调结果处理
5.1 authResults 数组语义
result.authResults 是与 permissions 入参一一对应的整型数组:
| 值 | 含义 |
|---|---|
0 |
授权成功(PERMISSION_GRANTED) |
-1 |
拒绝授权(PERMISSION_DENIED) |
-2 |
未找到该权限(检查 module.json5 声明) |
-3 |
标记为不再询问(用户勾选了"不再询问") |
5.2 遍历检查策略
在 PermissionGuard 中,使用遍历方式逐个检查结果:
typescript
for (let i = 0; i < result.authResults.length; i++) {
if (result.authResults[i] !== 0) {
return { granted: false, message: deniedMessage };
}
}
采用 "任意一个拒绝则整体拒绝" 策略------只要有一个权限被拒绝,就返回 granted: false。这在申请单个权限时用循环看似多余,但为未来"一次申请多个权限"的场景预留了扩展性。
5.3 异常兜底
typescript
catch (error) {
return { granted: false, message: deniedMessage };
}
如果 requestPermissionsFromUser 本身抛出异常(例如 context 无效、权限列表为空等),catch 块同样返回 granted: false。这样调用方只需要判断 granted 一个字段,无需关心内部是"用户拒绝"还是"系统异常"。
六、敏感权限申请策略(CAMERA / MICROPHONE)
6.1 CAMERA 权限
相机权限是项目中最重要的敏感权限。其申请流程在 requestCamera 中封装:
typescript
static async requestCamera(context: common.UIAbilityContext): Promise<PermissionResult> {
return PermissionGuard.request(context, ['ohos.permission.CAMERA'],
'请在设置里打开相机权限后继续');
}
当用户在 CreationComponents 中点击"拍照采集"按钮时,触发完整的权限检查链路:
typescript
private async openCamera() {
const context = getContext(this) as common.UIAbilityContext;
const permissionResult: PermissionResult = await PermissionGuard.requestCamera(context);
if (!permissionResult.granted) {
this.noticeText = permissionResult.message;
return;
}
// 继续打开系统相机
context.startAbilityForResult({
action: 'ohos.want.action.imageCapture'
}).then(/* ... */);
}
6.2 MICROPHONE 权限
麦克风权限的申请模式与相机完全一致:
typescript
static async requestMicrophone(context: common.UIAbilityContext): Promise<PermissionResult> {
return PermissionGuard.request(context, ['ohos.permission.MICROPHONE'],
'请在设置里打开麦克风权限后继续');
}
6.3 敏感权限申请的最佳实践
| 原则 | 说明 |
|---|---|
| 最小化 | 只申请当前功能必需的权限,不提前申请未使用的权限 |
| 场景化 | 在用户即将使用该功能时才申请,而非应用启动时 |
| 可解释 | 拒绝后给出明确的引导提示,告知用户为何需要该权限 |
| 降级处理 | 权限被拒后,功能应优雅降级而非直接崩溃 |
项目完全遵循了这些原则------requestCamera 只在用户点击"拍照采集"按钮时才调用,而不是在 aboutToAppear 中提前申请。拒绝后通过 noticeText 告知用户如何打开权限。
七、PhotoViewPicker 的 Scope 作用域访问模式
7.1 相册选择的权限特殊性
值得注意的是,PermissionGuard.requestAlbum 的实现非常特殊:
typescript
static async requestAlbum(context: common.UIAbilityContext): Promise<PermissionResult> {
// PhotoViewPicker grants scoped access to the selected media item.
// Do not declare broad media-read permissions here;
// some devices reject them at install time.
return { granted: true, message: '' };
}
它直接返回授权成功,不申请任何权限 。这是因为 PhotoViewPicker 采用了 Scope 作用域访问模式。
7.2 Scope 访问模式
传统权限模型(如 Android)中,访问相册需要声明 READ_MEDIA_IMAGES 权限,这意味着应用可以读取用户相册中所有图片。而 HarmonyOS 的 PhotoViewPicker 打破了这种"全有或全无"的模式:
传统权限模型:
声明 READ_MEDIA → 用户授权 → 应用可读取整个媒体库
Scope 访问模型(PhotoViewPicker):
启动 Picker → 用户选择 → 应用仅可访问被选中的文件
这种设计的好处:
- 隐私保护:应用只能访问用户明确选中的文件
- 权限简化:开发者无需处理繁重的媒体库权限
- 安装兼容 :部分设备会在安装时拒绝
READ_MEDIA声明,Scope 模式避免了这个问题
7.3 何时需要 READ_MEDIA
如果应用需要通过文件路径直接访问媒体文件(而非通过 Picker 选择),则需要声明 ohos.permission.READ_MEDIA。但在"画伴梦工厂"中,所有相册选择都通过 PhotoViewPicker 完成,因此完全不需要该权限。
八、权限拒绝后的用户引导
8.1 noticeText 显示机制
当权限被拒绝时,项目通过 noticeText 向用户展示提示信息:
typescript
// PermissionGuard 返回的 deniedMessage 直接被赋给 noticeText
this.noticeText = permissionResult.message; // 如 "请在设置里打开相机权限后继续"
noticeText 是 @State 变量,在 UI 中绑定到通知栏组件:
typescript
// 假设的 NoticeBar 结构
Text(this.noticeText)
.fontColor('#D94C3D') // 红色警示
.fontSize(14)
.visibility(this.noticeText !== '' ? Visibility.Visible : Visibility.Hidden)
8.2 引导逻辑
当用户首次拒绝权限后,后续再次触发同功能时,系统弹窗可能不再出现(用户勾选了"不再询问")。此时需要引导用户前往系统设置中手动开启。
目前的 PermissionGuard 将所有拒绝场景统一输出 deniedMessage。更完善的方案可以增加对 authResults[i] === -3(不再询问)的区分处理:
typescript
if (result.authResults[i] === -3) {
return {
granted: false,
message: '已拒绝权限并不再询问,请前往「设置 > 应用 > 画伴梦工厂 > 权限」中手动开启'
};
}
8.3 完整时序
用户点击"拍照采集"
│
▼
PermissionGuard.requestCamera()
│
├── 用户授权
│ └── granted: true → 启动系统相机
│
├── 用户拒绝(单次)
│ └── granted: false → noticeText = "请在设置里打开相机权限后继续"
│
└── 用户拒绝(不再询问)
└── granted: false → noticeText = "已拒绝并不再询问,请前往设置中手动开启"
九、完整代码与单元职责
9.1 PermissionGuard.ets 完整源码
typescript
import { abilityAccessCtrl, common, Permissions } from '@kit.AbilityKit';
export interface PermissionResult {
granted: boolean;
message: string;
}
export class PermissionGuard {
static async requestCamera(context: common.UIAbilityContext): Promise<PermissionResult> {
return PermissionGuard.request(context, ['ohos.permission.CAMERA'],
'请在设置里打开相机权限后继续');
}
static async requestAlbum(context: common.UIAbilityContext): Promise<PermissionResult> {
return { granted: true, message: '' };
}
static async requestMicrophone(context: common.UIAbilityContext): Promise<PermissionResult> {
return PermissionGuard.request(context, ['ohos.permission.MICROPHONE'],
'请在设置里打开麦克风权限后继续');
}
private static async request(context: common.UIAbilityContext,
permissions: Permissions[], deniedMessage: string): Promise<PermissionResult> {
try {
const manager = abilityAccessCtrl.createAtManager();
const result = await manager.requestPermissionsFromUser(context, permissions);
for (let i = 0; i < result.authResults.length; i++) {
if (result.authResults[i] !== 0) {
return { granted: false, message: deniedMessage };
}
}
return { granted: true, message: '' };
} catch (error) {
return { granted: false, message: deniedMessage };
}
}
}
9.2 各方法职责总览
| 方法 | 类型 | 权限 | 返回 deniedMessage | 说明 |
|---|---|---|---|---|
requestCamera |
public static | ohos.permission.CAMERA |
"请在设置里打开相机权限后继续" | 调用系统相机前使用 |
requestAlbum |
public static | 无(Scope 模式) | ""(空字符串) | PhotoViewPicker 无需额外权限 |
requestMicrophone |
public static | ohos.permission.MICROPHONE |
"请在设置里打开麦克风权限后继续" | 需要录音功能时使用 |
request |
private static | 任意 Permissions[] |
由上层传入 | 统一的权限请求实现 |
十、最佳实践总结
10.1 权限管理架构建议
- 抽取独立服务 :将权限逻辑从 UI 组件中分离到独立服务(如
PermissionGuard),便于维护和复用 - 统一返回值 :定义统一的
PermissionResult接口,降低调用方的复杂度 - 异常安全 :使用 try-catch 包裹
requestPermissionsFromUser,防止异常导致 UI 卡死 - 差异化提示 :根据
authResults[i]的值给出不同的提示文案
10.2 敏感权限设计原则
- 场景触发:在用户即将使用功能时申请,而非应用启动时预申请
- 一次一个:尽量一次只申请一个敏感权限,避免多个弹窗连续出现
- 降级路径:权限被拒后提供替代方案(如示例画作),而不是直接阻断流程
- Scope 优先 :优先使用
PhotoViewPicker等 Scope 访问 API,减少敏感权限依赖
10.3 常见陷阱
| 陷阱 | 解决方案 |
|---|---|
忘记在 module.json5 中声明权限 |
在 module.json5 的 requestPermissions 数组中添加权限声明 |
| context 类型不匹配 | 使用 getContext(this) as common.UIAbilityContext 进行类型转换 |
| 一次申请过多权限 | 按功能模块拆分,只在需要时申请对应权限 |
| 忽略"不再询问"状态 | 检测 authResults[i] === -3,引导用户前往设置手动开启 |
总结
本文通过"画伴梦工厂"的 PermissionGuard 服务,系统梳理了 HarmonyOS 权限管理的核心知识点:
| 知识点 | 实现方式 |
|---|---|
| 权限管理入口 | abilityAccessCtrl.createAtManager() |
| 动态权限请求 | manager.requestPermissionsFromUser(context, permissions) |
| 异步模式 | async/await + Promise |
| 结果判断 | 遍历 authResults,0 为授权,非 0 为拒绝 |
| 敏感权限策略 | 场景触发、最小化申请、优雅降级 |
| Scope 访问 | PhotoViewPicker 无需 READ_MEDIA 权限 |
| 拒绝引导 | 通过 noticeText 展示提示信息 |
下一节我们将探讨 HarmonyOS 的隐私合规与数据保护,了解如何构建更安全的用户数据访问机制。
参考源码
本文所有代码均来自项目文件:
products/default/src/main/ets/services/PermissionGuard.ets--- 权限管理服务,封装 abilityAccessCtrl 核心操作products/default/src/main/ets/components/CreationComponents.ets--- 创作组件,演示权限申请与 UI 的集成