HarmonyOS APP<<古今职鉴定>>开源教程第17篇:权限管理:安全与用户体验的平衡
本篇学习鸿蒙权限体系,实现优雅的权限申请流程
·
本篇学习鸿蒙权限体系,实现优雅的权限申请流程

图:权限管理:安全与用户体验的平衡 的关键流程与实现要点。
学习目标
完成本篇后,你将能够:
- ✅ 理解鸿蒙权限体系
- ✅ 实现权限检查与申请
- ✅ 封装权限工具类
- ✅ 处理权限拒绝场景
预计学习时间
约 90 分钟
实战一:理解权限体系
第一步:权限分类
| 类型 | 说明 | 示例 |
|---|---|---|
| system_grant | 系统授权,安装时自动授予 | 网络访问 |
| user_grant | 用户授权,需要运行时申请 | 相机、位置 |
第二步:权限等级
| 等级 | 说明 |
|---|---|
| normal | 普通权限,风险较低 |
| restricted | 受限权限,需要审核 |
| dangerous | 危险权限,涉及隐私 |
第三步:常用权限
| 权限 | 说明 | 类型 |
|---|---|---|
| ohos.permission.INTERNET | 网络访问 | system_grant |
| ohos.permission.CAMERA | 相机 | user_grant |
| ohos.permission.LOCATION | 位置 | user_grant |
| ohos.permission.DETECT_GESTURE | 手势检测 | user_grant |
| ohos.permission.NFC_TAG | NFC | user_grant |
实战二:声明权限
第一步:在 module.json5 中声明
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.CAMERA",
"reason": "$string:camera_permission_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.LOCATION",
"reason": "$string:location_permission_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
}
]
}
}
第二步:添加权限说明
在 resources/base/element/string.json 中:
{
"string": [
{
"name": "camera_permission_reason",
"value": "用于拍摄官职名片照片"
},
{
"name": "location_permission_reason",
"value": "用于显示附近的历史遗迹"
}
]
}
第三步:usedScene 配置
| 字段 | 说明 |
|---|---|
| abilities | 使用该权限的 Ability |
| when | inuse(使用时)/ always(始终) |
实战三:检查和申请权限
第一步:导入模块
import { abilityAccessCtrl, bundleManager, Permissions, common } from '@kit.AbilityKit';
第二步:检查权限状态
async function checkPermission(permission: Permissions): Promise<boolean> {
try {
// 获取应用信息
const bundleInfo = await bundleManager.getBundleInfoForSelf(
bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION
);
const tokenId = bundleInfo.appInfo.accessTokenId;
// 创建权限管理器
const atManager = abilityAccessCtrl.createAtManager();
// 检查权限
const grantStatus = atManager.checkAccessTokenSync(tokenId, permission);
return grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
} catch (error) {
console.error('检查权限失败:', error);
return false;
}
}
第三步:申请权限
async function requestPermission(
context: common.UIAbilityContext,
permissions: Permissions[]
): Promise<boolean> {
try {
const atManager = abilityAccessCtrl.createAtManager();
const result = await atManager.requestPermissionsFromUser(context, permissions);
// 检查所有权限是否都已授予
return result.authResults.every(
status => status === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED
);
} catch (error) {
console.error('申请权限失败:', error);
return false;
}
}
第四步:完整流程
async function ensurePermission(
context: common.UIAbilityContext,
permission: Permissions
): Promise<boolean> {
// 先检查
const hasPermission = await checkPermission(permission);
if (hasPermission) {
return true;
}
// 再申请
return await requestPermission(context, [permission]);
}
实战四:封装权限工具类
第一步:创建 PermissionUtil
// common/PermissionUtil.ets
import { abilityAccessCtrl, bundleManager, Permissions, common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
export class PermissionUtil {
/**
* 检查单个权限
*/
static async check(permission: Permissions): Promise<boolean> {
try {
const bundleInfo = await bundleManager.getBundleInfoForSelf(
bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION
);
const tokenId = bundleInfo.appInfo.accessTokenId;
const atManager = abilityAccessCtrl.createAtManager();
const grantStatus = atManager.checkAccessTokenSync(tokenId, permission);
return grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
} catch {
return false;
}
}
/**
* 检查多个权限
*/
static async checkMultiple(permissions: Permissions[]): Promise<Map<Permissions, boolean>> {
const result = new Map<Permissions, boolean>();
for (const permission of permissions) {
result.set(permission, await this.check(permission));
}
return result;
}
/**
* 申请权限
*/
static async request(
context: common.UIAbilityContext,
permissions: Permissions[]
): Promise<boolean> {
try {
const atManager = abilityAccessCtrl.createAtManager();
const result = await atManager.requestPermissionsFromUser(context, permissions);
return result.authResults.every(
status => status === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED
);
} catch {
return false;
}
}
/**
* 确保权限(检查+申请)
*/
static async ensure(
context: common.UIAbilityContext,
permission: Permissions
): Promise<boolean> {
const hasPermission = await this.check(permission);
if (hasPermission) return true;
return await this.request(context, [permission]);
}
/**
* 打开应用设置页
*/
static async openSettings(context: common.UIAbilityContext): Promise<void> {
const want: Want = {
bundleName: 'com.huawei.hmos.settings',
abilityName: 'com.huawei.hmos.settings.MainAbility',
uri: 'application_info_entry',
parameters: {
pushParams: context.abilityInfo.bundleName
}
};
try {
await context.startAbility(want);
} catch (error) {
console.error('打开设置失败:', error);
}
}
}
第二步:使用工具类
import { PermissionUtil } from '../common/PermissionUtil';
@Entry
@Component
struct Lesson17Page {
@State hasPermission: boolean = false;
async aboutToAppear() {
const permission: Permissions = 'ohos.permission.CAMERA';
this.hasPermission = await PermissionUtil.check(permission);
}
async requestCameraPermission() {
const context = getContext(this) as common.UIAbilityContext;
const permission: Permissions = 'ohos.permission.CAMERA';
this.hasPermission = await PermissionUtil.ensure(context, permission);
}
openSettings() {
const context = getContext(this) as common.UIAbilityContext;
PermissionUtil.openSettings(context);
}
}
实战五:处理权限拒绝
第一步:设计引导页面
@Entry
@Component
struct Lesson17Page {
@State permissionStatus: 'unknown' | 'granted' | 'denied' = 'unknown';
@State showGuide: boolean = false;
async aboutToAppear() {
await this.checkPermissionStatus();
}
async checkPermissionStatus() {
const permission: Permissions = 'ohos.permission.DETECT_GESTURE';
const hasPermission = await PermissionUtil.check(permission);
this.permissionStatus = hasPermission ? 'granted' : 'denied';
}
build() {
Column() {
// 头部
Row() {
Text('权限管理')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor('#1e293b')
}
.width('100%')
.height(56)
.padding({ left: 16, right: 16 })
.backgroundColor(Color.White)
// 内容
Column({ space: 20 }) {
if (this.permissionStatus === 'granted') {
this.GrantedView()
} else if (this.permissionStatus === 'denied') {
this.DeniedView()
} else {
this.LoadingView()
}
}
.width('100%')
.layoutWeight(1)
.justifyContent(FlexAlign.Center)
.padding(20)
}
.width('100%')
.height('100%')
.backgroundColor('#f8f6f5')
}
@Builder
LoadingView() {
Column({ space: 16 }) {
LoadingProgress()
.width(48)
.height(48)
Text('检查权限中...')
.fontSize(14)
.fontColor('#64748b')
}
}
@Builder
GrantedView() {
Column({ space: 16 }) {
Image($r('app.media.ic_check_circle'))
.width(64)
.height(64)
.fillColor('#22c55e')
Text('权限已授予')
.fontSize(18)
.fontColor('#1e293b')
Text('您可以正常使用握姿感应功能')
.fontSize(14)
.fontColor('#64748b')
}
}
@Builder
DeniedView() {
Column({ space: 16 }) {
Image($r('app.media.ic_lock'))
.width(64)
.height(64)
.fillColor('#64748b')
Text('需要权限')
.fontSize(18)
.fontColor('#1e293b')
Text('握姿感应功能需要手势检测权限')
.fontSize(14)
.fontColor('#64748b')
.textAlign(TextAlign.Center)
Button('授予权限')
.width('80%')
.height(44)
.backgroundColor('#c41e3a')
.margin({ top: 20 })
.onClick(async () => {
await this.requestPermission();
})
Text('如果无法授权,请前往设置手动开启')
.fontSize(12)
.fontColor('#9ca3af')
.margin({ top: 12 })
.onClick(() => {
this.openSettings();
})
.decoration({ type: TextDecorationType.Underline })
}
}
async requestPermission() {
const context = getContext(this) as common.UIAbilityContext;
const permission: Permissions = 'ohos.permission.DETECT_GESTURE';
const granted = await PermissionUtil.ensure(context, permission);
this.permissionStatus = granted ? 'granted' : 'denied';
}
openSettings() {
const context = getContext(this) as common.UIAbilityContext;
PermissionUtil.openSettings(context);
}
}
@Builder
export function Lesson17PageBuilder() {
Lesson17Page()
}
第二步:运行验证
hvigorw assembleHap --no-daemon
完整代码
完整代码见上方实战五。
本课小结
核心知识点
| 知识点 | 说明 |
|---|---|
| system_grant | 系统自动授权 |
| user_grant | 需要用户授权 |
| checkAccessTokenSync | 同步检查权限 |
| requestPermissionsFromUser | 申请权限 |
| startAbility | 打开设置页 |
权限申请最佳实践
- 只在需要时申请
- 说明权限用途
- 处理拒绝场景
- 提供设置入口
- 优雅降级
课后练习
练习1:批量权限申请
实现一次申请多个权限的流程。
练习2:权限状态监听
监听权限状态变化,实时更新 UI。
下一课预告
第18课我们将学习页面路由与导航架构,包括:
- Navigation 路由系统
- NavPathStack 路由栈
- 路由参数传递
- 导航架构设计
项目开源地址
更多推荐



所有评论(0)