#跟着晓明学鸿蒙# 鸿蒙系统开发高级教程 - 权限管理与安全机制实战指南
本文介绍鸿蒙操作系统的权限管理体系和安全架构,帮助开发者正确理解和应用权限机制,保障应用安全。
1. 鸿蒙系统权限模型概述
鸿蒙操作系统采用了精细化的权限管理机制,基于"最小权限原则"设计,有效保障用户隐私和系统安全。与传统Android系统相比,鸿蒙系统权限模型具有以下特点:
- 分级权限架构:将权限分为系统权限、普通权限和用户授权权限三级
- 运行时权限管控:敏感权限需在运行时动态申请
- 权限使用透明化:系统记录并展示应用权限使用情况
- 细粒度权限控制:支持精确到特定场景的权限管理
1.1 权限分类
鸿蒙系统权限主要分为以下几类:
- 系统权限:仅系统应用和系统服务可申请的高权限
- 普通权限:对用户隐私和系统安全影响较小的权限,安装时自动授予
- 用户授权权限:涉及用户隐私的权限,需用户明确授权
权限类型 | 申请方式 | 授权时机 | 授权范围 |
---|---|---|---|
系统权限 | 配置文件声明 | 系统预置 | 系统组件 |
普通权限 | 配置文件声明 | 应用安装时 | 所有应用 |
用户授权权限 | 配置文件声明+代码申请 | 运行时用户授权 | 经用户批准的应用 |
1.2 权限级别
鸿蒙系统在内部实现上进一步将权限细分为以下级别:
- normal:基础权限,安装时自动授予
- system_basic:基础系统权限,系统应用可获取
- system_core:核心系统权限,预装系统应用可获取
- internal:内部权限,仅供系统内核使用
2. 应用权限声明与配置
2.1 在module.json中声明权限
在开发鸿蒙应用时,需要在module.json
文件中声明应用所需的权限:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.READ_CONTACTS",
"reason": "需要读取联系人信息以提供社交功能",
"usedScene": {
"ability": [
"MainAbility"
],
"when": "always"
}
},
{
"name": "ohos.permission.CAMERA",
"reason": "需要使用相机拍摄照片",
"usedScene": {
"ability": [
"MainAbility"
],
"when": "inuse"
}
}
]
}
}
权限声明中的关键属性说明:
- name:权限的名称
- reason:申请权限的原因,用于向用户解释
- usedScene:权限使用场景
- ability:使用该权限的Ability组件
- when:权限使用时机(always-始终/inuse-仅前台使用)
2.2 常用权限列表
以下是鸿蒙系统中常用的一些权限:
权限名称 | 说明 | 权限类型 |
---|---|---|
ohos.permission.READ_CONTACTS | 读取联系人 | 用户授权 |
ohos.permission.WRITE_CONTACTS | 写入联系人 | 用户授权 |
ohos.permission.READ_CALENDAR | 读取日历 | 用户授权 |
ohos.permission.CAMERA | 使用相机 | 用户授权 |
ohos.permission.READ_MEDIA | 读取媒体文件 | 用户授权 |
ohos.permission.WRITE_MEDIA | 写入媒体文件 | 用户授权 |
ohos.permission.LOCATION | 获取位置信息 | 用户授权 |
ohos.permission.MICROPHONE | 使用麦克风 | 用户授权 |
ohos.permission.INTERNET | 网络访问 | 普通权限 |
ohos.permission.BLUETOOTH | 蓝牙通信 | 普通权限 |
3. 运行时权限申请
对于用户授权权限,应用需要在运行时动态申请。HarmonyOS提供了abilityAccessCtrl
模块用于权限管理。
3.1 权限申请流程
权限申请的一般流程如下:
- 检查应用是否已获得权限
- 如未获得权限,向用户申请
- 处理用户的授权结果
3.2 权限申请代码示例
3.2.1 引入权限管理模块
import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
import { BusinessError } from '@ohos.base';
3.2.2 检查权限状态
async checkPermission(permission: string): Promise<boolean> {
let atManager = abilityAccessCtrl.createAtManager();
try {
// 获取应用程序的accessTokenID
let tokenId = this.getContext().getApplicationInfo().accessTokenId;
// 检查应用是否已被授予权限
let result = await atManager.checkAccessToken(tokenId, permission);
console.info(`Permission ${permission} granted: ${result}`);
return result === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
} catch (err) {
let error = err as BusinessError;
console.error(`Failed to check permission ${permission}, error: ${error.code}, ${error.message}`);
return false;
}
}
3.2.3 申请单个权限
async requestPermission(permission: string): Promise<boolean> {
// 先检查权限是否已授予
let hasPermission = await this.checkPermission(permission);
if (hasPermission) {
return true;
}
// 如未授予,向用户申请权限
let atManager = abilityAccessCtrl.createAtManager();
try {
let result = await atManager.requestPermissionsFromUser(this.getContext(), [permission]);
// 处理授权结果
if (result.authResults[0] === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
console.info(`Permission ${permission} granted by user`);
return true;
} else {
console.info(`Permission ${permission} denied by user`);
return false;
}
} catch (err) {
let error = err as BusinessError;
console.error(`Failed to request permission ${permission}, error: ${error.code}, ${error.message}`);
return false;
}
}
3.2.4 批量申请权限
async requestMultiplePermissions(permissions: string[]): Promise<Map<string, boolean>> {
let grantResults = new Map<string, boolean>();
// 检查每个权限的授权状态
for (let permission of permissions) {
let granted = await this.checkPermission(permission);
grantResults.set(permission, granted);
}
// 筛选出未授权的权限
let permissionsToRequest = Array.from(grantResults.entries())
.filter(([_, granted]) => !granted)
.map(([permission, _]) => permission);
if (permissionsToRequest.length === 0) {
// 所有权限已授予
return grantResults;
}
// 向用户申请未授权的权限
let atManager = abilityAccessCtrl.createAtManager();
try {
let result = await atManager.requestPermissionsFromUser(this.getContext(), permissionsToRequest);
// 更新授权结果
for (let i = 0; i < permissionsToRequest.length; i++) {
let permission = permissionsToRequest[i];
let granted = result.authResults[i] === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
grantResults.set(permission, granted);
}
return grantResults;
} catch (err) {
let error = err as BusinessError;
console.error(`Failed to request permissions, error: ${error.code}, ${error.message}`);
return grantResults;
}
}
3.2.5 在Ability中使用
以相机权限为例:
import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
import { BusinessError } from '@ohos.base';
import camera from '@ohos.multimedia.camera';
@Entry
@Component
struct CameraPage {
@State hasPermission: boolean = false;
aboutToAppear() {
this.requestCameraPermission();
}
async requestCameraPermission() {
// 创建权限管理对象
let atManager = abilityAccessCtrl.createAtManager();
let tokenId = this.getContext().getApplicationInfo().accessTokenId;
try {
// 检查相机权限
let result = await atManager.checkAccessToken(tokenId, 'ohos.permission.CAMERA');
if (result === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
this.hasPermission = true;
this.initCamera();
return;
}
// 申请相机权限
let requestResult = await atManager.requestPermissionsFromUser(
this.getContext(),
['ohos.permission.CAMERA']
);
if (requestResult.authResults[0] === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
this.hasPermission = true;
this.initCamera();
} else {
promptAction.showToast({
message: '没有相机权限,无法使用相机功能',
duration: 2000
});
}
} catch (err) {
let error = err as BusinessError;
console.error(`相机权限请求失败: ${error.code}, ${error.message}`);
}
}
initCamera() {
// 初始化相机逻辑
console.info('相机初始化');
// ...相机初始化代码...
}
build() {
Column() {
if (this.hasPermission) {
// 显示相机预览
Text('相机预览')
.fontSize(20)
} else {
// 显示权限提示
Text('需要相机权限才能使用此功能')
.fontSize(16)
Button('授权相机权限')
.onClick(() => this.requestCameraPermission())
}
}
.width('100%')
.height('100%')
}
}
4. 权限最佳实践
4.1 权限使用原则
在开发鸿蒙应用时,应遵循以下权限使用原则:
- 最小权限原则:仅申请应用功能必需的权限
- 及时申请:在需要使用某项功能时才申请对应权限,而非一次性申请所有权限
- 明确说明:清晰解释申请权限的目的和用途
- 优雅降级:当用户拒绝权限时,提供替代方案或有限功能
4.2 权限拒绝处理
当用户拒绝授予权限时,应用应该做好以下处理:
async handlePermissionRequest(permission: string): Promise<void> {
let granted = await this.requestPermission(permission);
if (!granted) {
// 用户拒绝授权
// 1. 提供合理解释
promptAction.showDialog({
title: '权限说明',
message: `该功能需要${this.getPermissionDescription(permission)}权限才能正常使用。\n您可以在系统设置中手动授予此权限。`,
buttons: [
{
text: '稍后再说',
color: '#888888'
},
{
text: '前往设置',
color: '#0000ff'
}
],
success: (result) => {
if (result.index === 1) {
// 跳转到应用权限设置页面
this.openPermissionSettings();
}
}
});
// 2. 功能降级
this.enableLimitedFeatures();
} else {
// 权限授予,启用完整功能
this.enableFullFeatures();
}
}
// 获取权限的用户友好描述
getPermissionDescription(permission: string): string {
const permissionMap = {
'ohos.permission.CAMERA': '相机',
'ohos.permission.MICROPHONE': '麦克风',
'ohos.permission.LOCATION': '位置',
'ohos.permission.READ_CONTACTS': '读取联系人',
// 其他权限描述...
};
return permissionMap[permission] || permission;
}
// 跳转到应用权限设置
openPermissionSettings(): void {
// 通过bundle管理跳转到应用的权限设置页面
// 此处实现依赖于具体的API版本
}
// 启用有限功能(降级方案)
enableLimitedFeatures(): void {
// 实现应用的降级功能逻辑
}
// 启用完整功能
enableFullFeatures(): void {
// 实现应用的完整功能逻辑
}
4.3 不要频繁申请被拒绝的权限
避免多次申请同一个已被用户拒绝的权限,这会造成用户体验不佳。可以记录用户拒绝权限的次数:
// 存储用户拒绝权限的记录
class PermissionDenialTracker {
private static DENIAL_THRESHOLD = 2; // 最多申请次数
private static denialCounts = new Map<string, number>();
// 记录权限拒绝
static recordDenial(permission: string): void {
let count = this.denialCounts.get(permission) || 0;
this.denialCounts.set(permission, count + 1);
}
// 检查是否应该再次申请权限
static shouldRequestAgain(permission: string): boolean {
let count = this.denialCounts.get(permission) || 0;
return count < this.DENIAL_THRESHOLD;
}
// 重置拒绝计数
static resetDenialCount(permission: string): void {
this.denialCounts.set(permission, 0);
}
}
使用示例:
async smartRequestPermission(permission: string): Promise<boolean> {
// 检查是否已获得权限
let hasPermission = await this.checkPermission(permission);
if (hasPermission) {
return true;
}
// 检查是否超过拒绝阈值
if (!PermissionDenialTracker.shouldRequestAgain(permission)) {
// 已多次拒绝,直接引导用户前往设置
this.showPermissionSettingsGuide(permission);
return false;
}
// 申请权限
let granted = await this.requestPermission(permission);
if (!granted) {
// 记录拒绝
PermissionDenialTracker.recordDenial(permission);
} else {
// 权限获取成功,重置拒绝计数
PermissionDenialTracker.resetDenialCount(permission);
}
return granted;
}
// 显示权限设置引导
showPermissionSettingsGuide(permission: string): void {
promptAction.showDialog({
title: '需要权限',
message: `此功能需要${this.getPermissionDescription(permission)}权限。\n请在设置中手动授予权限。`,
buttons: [
{
text: '取消',
color: '#888888'
},
{
text: '前往设置',
color: '#0000ff'
}
],
success: (result) => {
if (result.index === 1) {
this.openPermissionSettings();
}
}
});
}
5. 鸿蒙系统安全架构
5.1 多层次安全防护
鸿蒙系统采用了多层次的安全防护体系:
- 系统层安全:微内核架构、特权分离、资源隔离
- 框架层安全:应用沙箱、进程间通信控制
- 应用层安全:权限管理、应用签名验证
- 数据层安全:加密存储、安全传输
5.2 应用签名与验证
鸿蒙系统要求所有应用必须经过签名才能安装运行。签名机制确保应用来源可信,且安装后不被篡改。
签名流程:
- 开发者创建密钥对
- 使用私钥对应用进行签名
- 系统使用公钥验证签名
签名不仅用于验证应用的完整性,还与应用身份和权限紧密相关:
- 相同开发者签名的应用可以共享数据和权限
- 系统权限只授予指定签名的应用
- 应用升级时要求签名一致
5.3 数据安全
鸿蒙系统提供了多种数据保护机制:
5.3.1 加密存储
import cryptoFramework from '@ohos.security.cryptoFramework';
// 数据加密示例
async encryptSensitiveData(plaintext: string, key: Uint8Array): Promise<string> {
try {
// 创建AES算法参数
let spec = {
algName: 'AES',
mode: 'GCM',
paddingName: 'PKCS7',
ivSize: 16,
engineMode: 'ANY'
};
// 生成初始化向量
let iv = cryptoFramework.generateRandom(16);
// 创建密码实例
let cipher = cryptoFramework.createCipher(spec);
// 初始化加密器
cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, key, iv);
// 执行加密
let plaintextBuffer = new Uint8Array(Buffer.from(plaintext, 'utf-8'));
let ciphertextBuffer = cipher.doFinal(plaintextBuffer);
// 组合IV和密文
let result = new Uint8Array(iv.length + ciphertextBuffer.length);
result.set(iv);
result.set(ciphertextBuffer, iv.length);
// 转为Base64字符串
return Buffer.from(result).toString('base64');
} catch (error) {
console.error(`加密失败: ${error}`);
throw error;
}
}
// 数据解密示例
async decryptSensitiveData(encryptedBase64: string, key: Uint8Array): Promise<string> {
try {
// 解析Base64数据
let encryptedData = new Uint8Array(Buffer.from(encryptedBase64, 'base64'));
// 提取IV (前16字节)
let iv = encryptedData.slice(0, 16);
// 提取密文
let ciphertext = encryptedData.slice(16);
// 创建AES算法参数
let spec = {
algName: 'AES',
mode: 'GCM',
paddingName: 'PKCS7',
ivSize: 16,
engineMode: 'ANY'
};
// 创建密码实例
let cipher = cryptoFramework.createCipher(spec);
// 初始化解密器
cipher.init(cryptoFramework.CryptoMode.DECRYPT_MODE, key, iv);
// 执行解密
let plaintextBuffer = cipher.doFinal(ciphertext);
// 转为字符串
return Buffer.from(plaintextBuffer).toString('utf-8');
} catch (error) {
console.error(`解密失败: ${error}`);
throw error;
}
}
5.3.2 安全偏好存储
对于需要安全存储的小型数据(如用户设置、令牌等),可以使用安全偏好存储:
import data_preferences from '@ohos.data.preferences';
import { BusinessError } from '@ohos.base';
class SecurePreferences {
private static instance: SecurePreferences;
private preferences: data_preferences.Preferences | null = null;
private securityKey: Uint8Array | null = null;
private constructor() {}
static getInstance(): SecurePreferences {
if (!this.instance) {
this.instance = new SecurePreferences();
}
return this.instance;
}
async init(context: Context): Promise<void> {
try {
// 获取或生成加密密钥
this.securityKey = await this.getOrCreateSecurityKey();
// 初始化首选项
this.preferences = await data_preferences.getPreferences(context, 'secureStorage');
} catch (err) {
let error = err as BusinessError;
console.error(`初始化安全存储失败: ${error.code}, ${error.message}`);
throw err;
}
}
// 安全存储字符串
async putString(key: string, value: string): Promise<void> {
if (!this.preferences || !this.securityKey) {
throw new Error('安全存储未初始化');
}
try {
// 加密数据
let encryptedValue = await this.encryptSensitiveData(value, this.securityKey);
// 存储加密后的数据
await this.preferences.put(key, encryptedValue);
await this.preferences.flush();
} catch (err) {
let error = err as BusinessError;
console.error(`安全存储失败: ${error.code}, ${error.message}`);
throw err;
}
}
// 从安全存储获取字符串
async getString(key: string, defaultValue: string = ''): Promise<string> {
if (!this.preferences || !this.securityKey) {
throw new Error('安全存储未初始化');
}
try {
// 获取加密数据
let encryptedValue = await this.preferences.get(key, '');
if (encryptedValue === '') {
return defaultValue;
}
// 解密数据
return await this.decryptSensitiveData(encryptedValue, this.securityKey);
} catch (err) {
let error = err as BusinessError;
console.error(`安全读取失败: ${error.code}, ${error.message}`);
return defaultValue;
}
}
// 获取或创建安全密钥
private async getOrCreateSecurityKey(): Promise<Uint8Array> {
// 实际应用中应使用系统密钥库存储密钥
// 此处仅为示例,使用固定密钥(实际应用不应这样做)
return new Uint8Array([
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
]);
}
// 加密和解密方法(与上面实现相同)
private async encryptSensitiveData(plaintext: string, key: Uint8Array): Promise<string> {
// 同上方实现
}
private async decryptSensitiveData(encryptedBase64: string, key: Uint8Array): Promise<string> {
// 同上方实现
}
}
6. 实战案例:构建安全的鸿蒙应用
本节将通过一个实际案例,演示如何在鸿蒙应用中正确实现权限管理和安全措施。
6.1 场景描述
假设我们开发一个健康追踪应用,需要使用以下权限:
- 位置信息:跟踪用户运动轨迹
- 活动识别:识别用户运动类型
- 存储权限:保存运动数据和照片
- 相机权限:记录运动照片
6.2 应用权限声明
在module.json
中声明所需权限:
{
"module": {
"package": "com.example.healthtracker",
"name": "HealthTrackerModule",
"requestPermissions": [
{
"name": "ohos.permission.LOCATION",
"reason": "需要获取位置信息以跟踪您的运动轨迹",
"usedScene": {
"ability": ["MainAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.ACTIVITY_MOTION",
"reason": "需要识别您的运动类型",
"usedScene": {
"ability": ["MainAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.READ_MEDIA",
"reason": "需要读取媒体文件以展示运动照片",
"usedScene": {
"ability": ["MainAbility"],
"when": "always"
}
},
{
"name": "ohos.permission.WRITE_MEDIA",
"reason": "需要保存您的运动照片和视频",
"usedScene": {
"ability": ["MainAbility"],
"when": "always"
}
},
{
"name": "ohos.permission.CAMERA",
"reason": "需要使用相机记录您的运动照片",
"usedScene": {
"ability": ["MainAbility"],
"when": "inuse"
}
}
]
}
}
6.3 权限管理类
创建一个专门的权限管理类,统一处理应用的权限申请和检查:
// PermissionManager.ets
import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
import { BusinessError } from '@ohos.base';
import common from '@ohos.app.ability.common';
export class PermissionManager {
private context: common.UIAbilityContext;
private denialCounts: Map<string, number> = new Map();
private static readonly DENIAL_THRESHOLD = 2;
constructor(context: common.UIAbilityContext) {
this.context = context;
}
// 检查单个权限
async checkPermission(permission: string): Promise<boolean> {
const atManager = abilityAccessCtrl.createAtManager();
try {
const tokenId = this.context.getApplicationInfo().accessTokenId;
const result = await atManager.checkAccessToken(tokenId, permission);
return result === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
} catch (err) {
const error = err as BusinessError;
console.error(`权限检查失败 ${permission}: ${error.code}, ${error.message}`);
return false;
}
}
// 申请单个权限
async requestPermission(permission: string): Promise<boolean> {
// 先检查是否已有权限
const hasPermission = await this.checkPermission(permission);
if (hasPermission) {
return true;
}
// 检查是否超过拒绝阈值
const denialCount = this.denialCounts.get(permission) || 0;
if (denialCount >= PermissionManager.DENIAL_THRESHOLD) {
this.showPermissionSettingsGuide(permission);
return false;
}
// 申请权限
const atManager = abilityAccessCtrl.createAtManager();
try {
const result = await atManager.requestPermissionsFromUser(this.context, [permission]);
const granted = result.authResults[0] === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
if (!granted) {
// 记录拒绝次数
this.denialCounts.set(permission, denialCount + 1);
}
return granted;
} catch (err) {
const error = err as BusinessError;
console.error(`权限申请失败 ${permission}: ${error.code}, ${error.message}`);
return false;
}
}
// 批量申请权限
async requestPermissions(permissions: string[]): Promise<Map<string, boolean>> {
const results = new Map<string, boolean>();
// 先检查所有权限状态
for (const permission of permissions) {
const granted = await this.checkPermission(permission);
results.set(permission, granted);
}
// 筛选未授权的权限
const permissionsToRequest = Array.from(results.entries())
.filter(([_, granted]) => !granted)
.map(([permission, _]) => permission);
if (permissionsToRequest.length === 0) {
return results;
}
// 逐个申请权限(为了更好的用户体验,避免一次性弹出多个权限申请框)
for (const permission of permissionsToRequest) {
const granted = await this.requestPermission(permission);
results.set(permission, granted);
}
return results;
}
// 显示权限设置引导
private showPermissionSettingsGuide(permission: string): void {
// 获取权限的用户友好名称
const permissionName = this.getPermissionFriendlyName(permission);
// 显示对话框引导用户前往设置
import promptAction from '@ohos.promptAction';
promptAction.showDialog({
title: '需要权限',
message: `此功能需要${permissionName}权限才能正常工作。\n\n请前往设置手动授予此权限。`,
buttons: [
{
text: '取消',
color: '#888888'
},
{
text: '前往设置',
color: '#0000ff'
}
],
success: (result) => {
if (result.index === 1) {
// 跳转到应用权限设置页面
this.openPermissionSettings();
}
}
});
}
// 获取权限的用户友好名称
private getPermissionFriendlyName(permission: string): string {
const permissionMap: Record<string, string> = {
'ohos.permission.LOCATION': '位置',
'ohos.permission.ACTIVITY_MOTION': '活动识别',
'ohos.permission.READ_MEDIA': '媒体访问',
'ohos.permission.WRITE_MEDIA': '媒体存储',
'ohos.permission.CAMERA': '相机'
};
return permissionMap[permission] || permission;
}
// 跳转到应用权限设置页面
private openPermissionSettings(): void {
// 使用bundle管理跳转到应用的权限设置页面
import bundleManager from '@ohos.bundle.bundleManager';
import common from '@ohos.app.ability.common';
import wantConstant from '@ohos.app.ability.wantConstant';
try {
const bundleName = this.context.getApplicationInfo().name;
const want = {
action: wantConstant.Action.ACTION_APPLICATION_DETAILS_SETTINGS,
entities: [wantConstant.Entity.ENTITY_HOME_SCREEN],
parameters: {
'bundle.name': bundleName
}
};
this.context.startAbility(want);
} catch (err) {
const error = err as BusinessError;
console.error(`跳转到权限设置失败: ${error.code}, ${error.message}`);
}
}
}
6.4 在页面中使用权限管理
在运动追踪页面中使用权限管理类:
// TrackingPage.ets
import { PermissionManager } from '../common/PermissionManager';
@Entry
@Component
struct TrackingPage {
private permissionManager: PermissionManager = new PermissionManager(getContext(this) as common.UIAbilityContext);
@State hasLocationPermission: boolean = false;
@State hasActivityPermission: boolean = false;
@State hasCameraPermission: boolean = false;
aboutToAppear() {
this.checkAndRequestPermissions();
}
async checkAndRequestPermissions() {
// 检查并申请所需权限
const permissions = [
'ohos.permission.LOCATION',
'ohos.permission.ACTIVITY_MOTION',
'ohos.permission.READ_MEDIA',
'ohos.permission.WRITE_MEDIA'
];
const results = await this.permissionManager.requestPermissions(permissions);
// 更新权限状态
this.hasLocationPermission = results.get('ohos.permission.LOCATION') || false;
this.hasActivityPermission = results.get('ohos.permission.ACTIVITY_MOTION') || false;
// 根据权限状态启用相应功能
if (this.hasLocationPermission) {
this.startLocationTracking();
}
if (this.hasActivityPermission) {
this.startActivityRecognition();
}
}
// 打开相机前检查相机权限
async openCamera() {
const granted = await this.permissionManager.requestPermission('ohos.permission.CAMERA');
this.hasCameraPermission = granted;
if (granted) {
// 打开相机逻辑
this.startCamera();
} else {
// 提示用户权限被拒绝
promptAction.showToast({
message: '无法使用相机功能,请授予相机权限',
duration: 2000
});
}
}
// 启动位置追踪
startLocationTracking() {
// 位置追踪实现...
}
// 启动活动识别
startActivityRecognition() {
// 活动识别实现...
}
// 启动相机
startCamera() {
// 相机功能实现...
}
build() {
Column() {
// 应用UI实现
Text('健康追踪')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 })
if (this.hasLocationPermission && this.hasActivityPermission) {
// 显示完整功能
Text('正在追踪您的活动...')
.fontSize(16)
.margin({ bottom: 10 })
Button('拍摄照片')
.onClick(() => this.openCamera())
.margin({ top: 20 })
} else {
// 显示有限功能和权限提示
Text('需要位置和活动识别权限才能使用完整功能')
.fontSize(16)
.margin({ bottom: 20 })
Button('授予必要权限')
.onClick(() => this.checkAndRequestPermissions())
}
}
.width('100%')
.height('100%')
.padding(20)
}
}
6.5 安全存储用户数据
创建一个安全的数据管理器来处理用户健康数据:
// HealthDataManager.ets
import { SecurePreferences } from '../common/SecurePreferences';
import data_rdb from '@ohos.data.relationalStore';
import { BusinessError } from '@ohos.base';
import cryptoFramework from '@ohos.security.cryptoFramework';
export class HealthDataManager {
private context: common.Context;
private securePrefs: SecurePreferences;
private database: data_rdb.RdbStore | null = null;
private encryptionKey: Uint8Array | null = null;
constructor(context: common.Context) {
this.context = context;
this.securePrefs = SecurePreferences.getInstance();
}
// 初始化数据管理器
async init(): Promise<boolean> {
try {
// 初始化安全偏好存储
await this.securePrefs.init(this.context);
// 获取或生成加密密钥
this.encryptionKey = await this.getOrCreateEncryptionKey();
// 初始化数据库
await this.initDatabase();
return true;
} catch (err) {
const error = err as BusinessError;
console.error(`数据管理器初始化失败: ${error.code}, ${error.message}`);
return false;
}
}
// 获取或创建加密密钥
private async getOrCreateEncryptionKey(): Promise<Uint8Array> {
try {
// 从安全存储中获取密钥
const storedKey = await this.securePrefs.getString('encryption_key', '');
if (storedKey !== '') {
// 解码已存储的密钥
return new Uint8Array(Buffer.from(storedKey, 'base64'));
}
// 生成新密钥
const newKey = cryptoFramework.generateRandom(32); // 256位密钥
// 存储密钥
await this.securePrefs.putString('encryption_key', Buffer.from(newKey).toString('base64'));
return newKey;
} catch (err) {
const error = err as BusinessError;
console.error(`加密密钥生成失败: ${error.code}, ${error.message}`);
throw err;
}
}
// 初始化数据库
private async initDatabase(): Promise<void> {
try {
const STORE_CONFIG = {
name: 'HealthData.db',
securityLevel: data_rdb.SecurityLevel.S1
};
// 创建数据库
this.database = await data_rdb.getRdbStore(this.context, STORE_CONFIG);
// 创建表
await this.database.executeSql(
'CREATE TABLE IF NOT EXISTS activities (' +
'id INTEGER PRIMARY KEY AUTOINCREMENT, ' +
'type TEXT NOT NULL, ' +
'start_time INTEGER NOT NULL, ' +
'duration INTEGER NOT NULL, ' +
'encrypted_data TEXT NOT NULL, ' +
'created_at INTEGER NOT NULL)'
);
} catch (err) {
const error = err as BusinessError;
console.error(`数据库初始化失败: ${error.code}, ${error.message}`);
throw err;
}
}
// 保存活动数据
async saveActivity(activityType: string, startTime: number, duration: number, activityData: Object): Promise<boolean> {
if (!this.database || !this.encryptionKey) {
throw new Error('数据管理器未初始化');
}
try {
// 加密活动数据
const dataJson = JSON.stringify(activityData);
const encryptedData = await this.encryptData(dataJson, this.encryptionKey);
// 准备值对象
const valueObject = {
type: activityType,
start_time: startTime,
duration: duration,
encrypted_data: encryptedData,
created_at: Date.now()
};
// 插入数据
await this.database.insert('activities', valueObject);
return true;
} catch (err) {
const error = err as BusinessError;
console.error(`保存活动数据失败: ${error.code}, ${error.message}`);
return false;
}
}
// 获取活动数据
async getActivities(limit: number = 20, offset: number = 0): Promise<Array<any>> {
if (!this.database || !this.encryptionKey) {
throw new Error('数据管理器未初始化');
}
try {
// 查询数据
const resultSet = await this.database.querySql(
'SELECT * FROM activities ORDER BY start_time DESC LIMIT ? OFFSET ?',
[limit, offset]
);
const activities = [];
while (resultSet.goToNextRow()) {
const id = resultSet.getLong(resultSet.getColumnIndex('id'));
const type = resultSet.getString(resultSet.getColumnIndex('type'));
const startTime = resultSet.getLong(resultSet.getColumnIndex('start_time'));
const duration = resultSet.getLong(resultSet.getColumnIndex('duration'));
const encryptedData = resultSet.getString(resultSet.getColumnIndex('encrypted_data'));
// 解密数据
const decryptedData = await this.decryptData(encryptedData, this.encryptionKey);
const activityData = JSON.parse(decryptedData);
activities.push({
id,
type,
startTime,
duration,
data: activityData
});
}
resultSet.close();
return activities;
} catch (err) {
const error = err as BusinessError;
console.error(`获取活动数据失败: ${error.code}, ${error.message}`);
return [];
}
}
// 加密数据
private async encryptData(data: string, key: Uint8Array): Promise<string> {
// 使用前面实现的加密方法
// ...
}
// 解密数据
private async decryptData(encryptedData: string, key: Uint8Array): Promise<string> {
// 使用前面实现的解密方法
// ...
}
}
7. 总结与最佳实践
鸿蒙系统的权限管理和安全机制是保障应用和用户数据安全的重要基础。本文介绍了鸿蒙系统权限的分类、申请方式和最佳实践,以及数据安全保护措施。
开发者应当遵循以下最佳实践:
- 最小权限原则:仅申请应用功能必须的权限
- 运行时权限处理:在合适的时机动态申请权限,并妥善处理权限拒绝情况
- 权限降级策略:当无法获得某些权限时,提供有限但仍可用的功能
- 数据加密存储:敏感数据应始终加密存储
- 透明度:向用户清晰解释为何需要权限以及如何使用数据
- 定期安全评估:定期检查应用的安全性并跟进最新的安全最佳实践
通过合理的权限管理和安全措施,开发可以构建既安全又用户友好的鸿蒙应用,为用户提供良好的使用体验同时保护他们的隐私和数据安全。
更多推荐
所有评论(0)