鸿蒙开发系列 · 中级篇
前言
上一篇我们写了一个天气速查元服务,功能跑通了,但有个问题没解决:如果它需要获取用户定位,该怎么申请位置权限?如果它需要读取通讯录来实现"分享给好友",系统会允许吗?如果它想访问相册选取图片,用户会看到怎样的授权弹窗?
在HarmonyOS NEXT(API 12+)中,安全架构经历了根本性的重构。Android那套"安装时一次性授权"的模型被彻底抛弃,取而代之的是更细粒度、更动态、更透明的权限管理体系。对于元服务而言,安全约束更加严格——部分高危权限直接对元服务关闭。
安全不是写完功能再"打补丁",而是贯穿整个开发周期的底线思维。本文将从应用签名、权限模型、ACL访问控制、隐私合规四个层面,帮你建立鸿蒙安全开发的完整认知。
一、HarmonyOS安全架构概览
1.1 四层安全体系
HarmonyOS的安全设计从芯片到应用分为四个层次:
┌─────────────────────────────────────────┐
│ 第4层:应用层安全 │
│ 签名验证、权限管控、隐私合规、数据加密 │
├─────────────────────────────────────────┤
│ 第3层:框架层安全 │
│ 沙箱隔离、IPC鉴权、Ability访问控制 │
├─────────────────────────────────────────┤
│ 第2层:系统层安全 │
│ 内核安全、文件系统加密、可信执行环境TEE │
├─────────────────────────────────────────┤
│ 第1层:硬件层安全 │
│ 安全芯片、可信启动、硬件加密引擎 │
└─────────────────────────────────────────┘
作为应用开发者,我们主要在第4层工作,但必须理解:鸿蒙的权限管控不是"框架层的一个模块",而是从硬件信任根一路延伸上来的完整链条。这也是为什么鸿蒙能做到不允许用户手动关闭权限管控——底层安全架构不给你绕过它的机会。
1.2 应用沙箱与隔离机制
每个HarmonyOS应用都运行在独立的沙箱中:
文件系统隔离:应用只能访问自己的
/data/app/el1/<bundleName>目录,无法直接读写其他应用的文件进程隔离:每个应用运行在独立的进程中,通过IPC(进程间通信)进行数据交换
网络隔离:应用默认只能访问公网,局域网通信需要额外申请权限
内存隔离:应用无法访问其他进程的内存空间
沙箱机制对开发者最直接的影响是:没有全局存储。Android那种"写个文件到SD卡大家都能读"的做法在HarmonyOS中行不通。数据共享必须通过系统提供的标准化接口(如Data Ability、分布式数据服务)。
二、应用签名机制
2.1 签名体系的三层结构
HarmonyOS采用三层签名体系,比Android的单一签名更严格:
开发者只需要关注第一层。当你用DevEco Studio构建HAP时,会自动使用你配置的数字证书进行签名。
2.2 数字证书配置实战
在DevEco Studio中配置签名:
方式一:自动签名(调试阶段推荐)
# File → Project Structure → Signing Configs → 勾选"Automatically generate signature"
DevEco Studio会自动生成调试证书,适合开发和联调阶段。
方式二:手动签名(发布必备)
正式上架必须使用手动签名,步骤如下:
生成密钥对:
keytool -genkeypair -alias "myapp" -keyalg EC -keysize 256 \ -sigalg SHA256withECDSA -dname "CN=MyCompany, O=MyOrg, C=CN" \ -keystore myapp.p12 -storetype pkcs12 -validity 3650生成证书请求文件(CSR):
keytool -certreq -alias "myapp" -keystore myapp.p12 -file myapp.csr在AppGallery Connect申请发布证书:
进入"证书、APP ID和Profile"
上传CSR,下载
.cer证书文件
配置Profile文件:
Profile(.p7b)是HarmonyOS特有的配置文件,包含:应用包名
允许申请的权限列表
证书绑定关系
调试/发布类型
在DevEco Studio中配置:
// build-profile.json5 { "app": { "signingConfigs": [ { "name": "release", "type": "HarmonyOS", "material": { "certpath": "/path/to/release.cer", "storePassword": "******", "keyAlias": "myapp", "keyPassword": "******", "profile": "/path/to/release.p7b", "signAlg": "SHA256withECDSA", "storeFile": "/path/to/myapp.p12" } } ] } }
2.3 签名校验的常见问题
三、权限管理模型
3.1 权限分级体系
HarmonyOS NEXT将权限分为三个等级:
对于元服务(Atomic Service),约束更严格:
normal权限:可正常使用
user_grant权限:可申请,但用户每次使用都可能被要求授权
system_grant权限:元服务原则上不可申请(个别白名单除外)
3.2 运行时权限申请实战
以定位权限为例,展示完整的申请流程:
// entry/src/main/ets/utils/PermissionManager.ets
import { abilityAccessCtrl, Permissions } from '@kit.AbilityKit';
import { promptAction } from '@kit.ArkUI';
export class PermissionManager {
// 检查权限是否已授予
static checkPermission(permission: Permissions): boolean {
const atManager = abilityAccessCtrl.createAtManager();
const authStatus = atManager.checkAccessTokenSync(permission);
return authStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
}
// 申请单个权限
static async requestPermission(permission: Permissions, reason: string): Promise<boolean> {
const atManager = abilityAccessCtrl.createAtManager();
// 先检查
if (this.checkPermission(permission)) {
return true;
}
// 申请授权
const authResults = await atManager.requestPermissionsFromUser(
getContext(),
[permission]
);
const granted = authResults.authResults[0] === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
if (!granted) {
promptAction.showToast({
message: `需要${reason}权限才能继续使用`
});
}
return granted;
}
// 批量申请权限
static async requestPermissions(
permissions: Array<{ perm: Permissions; reason: string }>
): Promise<boolean> {
const needRequest = permissions.filter(p => !this.checkPermission(p.perm));
if (needRequest.length === 0) {
return true;
}
const atManager = abilityAccessCtrl.createAtManager();
const authResults = await atManager.requestPermissionsFromUser(
getContext(),
needRequest.map(p => p.perm)
);
const allGranted = authResults.authResults.every(
r => r === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED
);
if (!allGranted) {
const denied = authResults.permissions.filter((_, i) =>
authResults.authResults[i] !== abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED
);
promptAction.showToast({
message: `以下权限被拒绝:${denied.join(', ')}`
});
}
return allGranted;
}
}
在页面中使用:
// entry/src/main/ets/pages/LocationPage.ets
import { PermissionManager } from '../utils/PermissionManager';
import { geoLocationManager } from '@kit.LocationKit';
@Entry
@Component
struct LocationPage {
@State locationText: string = '点击获取位置';
async getLocation() {
const granted = await PermissionManager.requestPermission(
'ohos.permission.LOCATION',
'定位'
);
if (!granted) return;
try {
const location = await geoLocationManager.getCurrentLocation();
this.locationText = `纬度:${location.latitude.toFixed(4)},经度:${location.longitude.toFixed(4)}`;
} catch (err) {
this.locationText = '获取位置失败';
}
}
build() {
Column({ space: 20 }) {
Text(this.locationText)
.fontSize(16)
.fontColor('#333')
Button('获取当前位置')
.onClick(() => this.getLocation())
}
.width('100%')
.height('100%')
.padding(24)
}
}
3.3 权限配置:module.json5
所有申请的权限必须在 module.json5 中声明:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.INTERNET",
"reason": "$string:perm_internet_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "always"
}
},
{
"name": "ohos.permission.LOCATION",
"reason": "$string:perm_location_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.CAMERA",
"reason": "$string:perm_camera_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
}
]
}
}
when 字段有两个值:
always:后台也能使用该权限(如网络)inuse:只有应用在前台时才能使用(如定位、相机)
四、ACL访问控制与组件可见性
4.1 Ability的exported属性
在HarmonyOS中,每个Ability默认是私有的,其他应用无法直接启动它。如果需要暴露给外部,必须显式设置 exported: true:
{
"abilities": [
{
"name": "EntryAbility",
"exported": true,
"skills": [
{
"actions": ["action.system.home"],
"entities": ["entity.system.home"]
}
]
},
{
"name": "InternalAbility",
"exported": false
}
]
}
安全建议:最小暴露原则。只有真正需要被外部调用的Ability才设置exported,且务必配置skills过滤条件,防止被恶意应用随意唤起。
4.2 应用间数据共享的三种方式
由于沙箱隔离,应用间不能直接共享文件。HarmonyOS提供了三种标准化共享方式:
文件URI分享实战:
import { uniformTypeDescriptor, fileUri } from '@kit.CoreFileKit';
import { wantConstant } from '@kit.AbilityKit';
async function shareImage(imagePath: string) {
const uri = fileUri.getUriFromPath(imagePath);
const want = {
action: wantConstant.Action.ACTION_SEND,
type: 'image/png',
parameters: {
[wantConstant.Params.PARAM_STREAM]: uri
}
};
const context = getContext();
await context.startAbility(want);
}
4.3 元服务的特殊安全约束
元服务由于免安装的特性,安全模型更加严格:
元服务权限黑名单(不可申请):
├── ohos.permission.READ_CONTACTS // 读取通讯录
├── ohos.permission.WRITE_CONTACTS // 写入通讯录
├── ohos.permission.READ_CALL_LOG // 读取通话记录
├── ohos.permission.WRITE_CALL_LOG // 写入通话记录
├── ohos.permission.READ_SMS // 读取短信
├── ohos.permission.SEND_SMS // 发送短信
├── ohos.permission.INSTALL_BUNDLE // 安装应用
├── ohos.permission.UNINSTALL_BUNDLE // 卸载应用
└── ohos.permission.SET_TIME // 设置系统时间
如果你的元服务确实需要通讯录相关能力,正确做法是:
调用系统提供的系统Picker(如联系人选择器)
由用户在系统UI中主动选择,而非直接读取
// 调用系统联系人选择器,而非直接申请READ_CONTACTS
import { contactPicker } from '@kit.ContactKit';
async function pickContact() {
const contact = await contactPicker.selectContact();
if (contact) {
console.info(`选中联系人:${contact.name}, ${contact.phoneNumber}`);
}
}
五、隐私合规与数据保护
5.1 隐私清单(Privacy Manifest)
HarmonyOS NEXT引入了隐私清单机制,要求开发者在 module.json5 中声明数据收集行为:
{
"module": {
"metadata": [
{
"name": "privacy_manifest",
"value": {
"dataCollected": [
{
"type": "location",
"purpose": "提供基于位置的天气服务",
"optional": false,
"shared": false
},
{
"type": "userContent",
"purpose": "用户主动上传的图片",
"optional": true,
"shared": false
}
],
"thirdPartySDKs": [
{
"name": "某某统计SDK",
"purpose": "应用数据分析"
}
]
}
}
]
}
}
这些信息会在应用市场的详情页展示,用户下载前就能清楚知道你的应用会收集哪些数据。
5.2 数据加密最佳实践
RDB数据库加密示例:
import { relationalStore } from '@kit.ArkData';
const config: relationalStore.StoreConfig = {
name: 'secure_db.db',
securityLevel: relationalStore.SecurityLevel.S3, // 加密存储
encrypt: true,
// 密钥由系统自动管理,无需手动传入
};
const store = await relationalStore.getRdbStore(getContext(), config);
六、小结与下篇预告
安全是HarmonyOS的基石,也是开发者最容易忽视的领域。本文从四个维度建立了完整认知:
签名机制:三层签名体系、数字证书配置、Profile文件管理
权限模型:normal/user_grant/system_grant三级划分、运行时动态申请
访问控制:exported最小暴露、应用间数据共享的标准化方式、元服务特殊约束
隐私合规:隐私清单声明、数据加密实践
安全机制保障的是"底线",而用户体验的上限则由系统能力的灵活调度决定。元服务和普通应用都需要在后台执行一些任务——定时刷新天气、接收推送消息、后台下载文件。第13篇将深入鸿蒙的推送服务与后台任务管理,讲解Push Kit、Work Scheduler、后台限制策略,以及如何在严格的系统约束下实现流畅的用户体验。
参考阅读
本系列第11篇:《元服务开发入门:从概念到第一个原子化服务》