HarmonyOS开发:系统权限提升——system与core权限
HarmonyOS开发:系统权限提升——system与core权限
📌 核心要点:鸿蒙的权限不是"申请了就有",而是分级的——normal、system、system_core三级权限对应不同的签名门槛,搞不清分级就等着满屏Permission denied。
背景与动机
你写了个系统应用,声明了一堆权限,结果运行的时候一半权限报"denied"。你明明有系统签名啊,为啥还不给?
因为你申请的那些权限里,有些是system级别的,有些是system_core级别的。系统签名只够用system级别的权限,system_core级别的权限需要平台签名才行。
这就好比你有公司的门禁卡,能进办公区,但进不了机房——机房需要更高级别的权限卡。
鸿蒙的权限分级是安全体系的根基。不理解这个分级,你连权限声明都写不对,更别提做系统级开发了。这篇就把权限分级、权限申请、权限提升策略和安全审计讲清楚。
核心原理
权限分级体系
鸿蒙的权限分三个级别:
| 级别 | 签名要求 | 授权方式 | 典型权限 |
|---|---|---|---|
normal |
无 | 安装时自动授权 | 网络访问、振动 |
system |
系统签名 | 安装时自动授权 | 设置时间、安装应用 |
system_core |
平台签名 | 安装时自动授权 | 卸载应用、写系统设置 |
注意看"授权方式"那一列——三个级别都是"安装时自动授权"。这意味着一旦你的签名级别够了,权限不需要用户手动授权,安装完就有了。
这也意味着:如果签名级别不够,你声明了也没用,安装时直接拒绝。
权限校验流程
flowchart TD
A[应用调用API] --> B{API需要权限?}
B -->|不需要| C[直接执行]
B -->|需要| D{权限级别?}
D -->|normal| E{已声明权限?}
D -->|system| F{已声明权限?}
D -->|system_core| G{已声明权限?}
E -->|是| H[检查签名: 任意签名]
E -->|否| I[Permission Denied]
F -->|是| J{签名类型 ≥ 系统签名?}
F -->|否| I
G -->|是| K{签名类型 ≥ 平台签名?}
G -->|否| I
H --> L[权限通过]
J -->|是| L
J -->|否| I
K -->|是| L
K -->|否| I
classDef check fill:#e3f2fd,stroke:#2196f3,color:#0d47a1
classDef pass fill:#e8f5e9,stroke:#4caf50,color:#1b5e20
classDef fail fill:#fce4ec,stroke:#f44336,color:#b71c1c
classDef level fill:#fff3e0,stroke:#ff9800,color:#e65100
class B,D check
class E,F,G,H,J,K level
class C,L pass
class I fail
特权权限列表
常用的system和system_core级别权限:
system级别(系统签名即可):
| 权限 | 说明 |
|---|---|
ohos.permission.SET_TIME |
设置系统时间 |
ohos.permission.SET_TIME_ZONE |
设置时区 |
ohos.permission.INSTALL_BUNDLE |
安装应用 |
ohos.permission.GET_BUNDLE_INFO_PRIVILEGED |
获取应用详细信息 |
ohos.permission.MANAGE_LOCAL_ACCOUNTS |
管理本地账号 |
ohos.permission.CONNECTIVITY_INTERNAL |
网络管理内部接口 |
system_core级别(需要平台签名):
| 权限 | 说明 |
|---|---|
ohos.permission.UNINSTALL_BUNDLE |
卸载应用 |
ohos.permission.WRITE_SYSTEM_SETTINGS |
写入系统设置 |
ohos.permission.FACTORY_RESET |
恢复出厂设置 |
ohos.permission.REBOOT |
重启设备 |
ohos.permission.MANAGE_USB |
USB管理 |
ohos.permission.UPDATE_SYSTEM |
系统更新 |
权限提升策略
有时候你的应用只有系统签名,但某个功能需要system_core权限。怎么办?
有三种策略:
- 拆分应用:把需要
system_core权限的功能拆到另一个有平台签名的应用里,通过IPC调用 - SA代理:把需要
system_core权限的操作封装成SA,SA运行在有平台签名的系统进程里 - 权限临时提升:通过
AccessTokenManager临时申请更高权限(需要系统签名+用户确认)
flowchart LR
A[系统签名应用] -->|需要core权限| B{选择策略}
B -->|拆分应用| C[平台签名应用]
C -->|IPC调用| D[执行core操作]
B -->|SA代理| E[平台签名SA]
E -->|samgr| D
B -->|临时提升| F[AccessTokenManager]
F -->|用户确认| G[临时core权限]
G --> D
classDef problem fill:#fce4ec,stroke:#f44336,color:#b71c1c
classDef strategy fill:#e3f2fd,stroke:#2196f3,color:#0d47a1
classDef solution fill:#e8f5e9,stroke:#4caf50,color:#1b5e20
class A problem
class B,C,E,F strategy
class D,G solution
代码实战
基础用法:权限声明与检查
// common/PermissionManager.ets - 权限管理工具
import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';
import bundleManager from '@ohos.bundle.bundleManager';
import { BusinessError } from '@ohos.base';
class PermissionManager {
private tag: string = 'PermissionManager';
// 检查单个权限是否已授权
async checkPermission(permission: Permissions): Promise<boolean> {
try {
const atManager = abilityAccessCtrl.createAtManager();
const bundleInfo = await bundleManager.getBundleInfoForSelf(
bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT
);
const tokenID = bundleInfo.appInfo.accessTokenId;
const result = await atManager.checkAccessToken(tokenID, permission);
return result === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
} catch (err) {
const error = err as BusinessError;
console.error(this.tag, `检查权限失败: ${error.message}`);
return false;
}
}
// 批量检查权限
async checkPermissions(permissions: Permissions[]): Promise<Map<Permissions, boolean>> {
const resultMap = new Map<Permissions, boolean>();
for (const permission of permissions) {
const granted = await this.checkPermission(permission);
resultMap.set(permission, granted);
}
return resultMap;
}
// 请求用户授权(仅对user_grant类型的权限有效)
async requestPermissions(
context: Context,
permissions: Permissions[]
): Promise<Map<Permissions, boolean>> {
const resultMap = new Map<Permissions, boolean>();
try {
const atManager = abilityAccessCtrl.createAtManager();
const result = await atManager.requestPermissionsFromUser(context, permissions);
for (let i = 0; i < permissions.length; i++) {
resultMap.set(permissions[i], result.authResults[i] === 0);
}
} catch (err) {
const error = err as BusinessError;
console.error(this.tag, `请求权限失败: ${error.message}`);
permissions.forEach(p => resultMap.set(p, false));
}
return resultMap;
}
}
export default new PermissionManager();
进阶用法:权限提升与IPC代理
系统签名应用需要调用system_core权限的API时,通过IPC代理实现:
// common/CorePermissionProxy.ets - core权限代理
import { rpc } from '@ohos.rpc';
import { systemAbilityManager } from '@ohos.systemAbilityManager';
// 定义核心操作SA的ID
const CORE_OPERATION_SA_ID = 0x9002;
// 核心操作请求码
const REQUEST_CODE_UNINSTALL_APP = 100;
const REQUEST_CODE_FACTORY_RESET = 101;
const REQUEST_CODE_REBOOT = 102;
const REQUEST_CODE_WRITE_SETTINGS = 103;
class CorePermissionProxy {
private tag: string = 'CorePermissionProxy';
private proxy: rpc.IRemoteObject | null = null;
// 获取核心操作SA的代理
async getProxy(): Promise<rpc.IRemoteObject | null> {
if (this.proxy) {
return this.proxy;
}
try {
this.proxy = await systemAbilityManager.getSystemAbility(CORE_OPERATION_SA_ID);
return this.proxy;
} catch (err) {
console.error(this.tag, `获取代理失败: ${JSON.stringify(err)}`);
return null;
}
}
// 通过SA代理卸载应用(system_core权限操作)
async uninstallApp(bundleName: string): Promise<boolean> {
const proxy = await this.getProxy();
if (!proxy) {
console.error(this.tag, '代理不可用');
return false;
}
try {
const option = new rpc.MessageOption();
const data = new rpc.MessageSequence();
const reply = new rpc.MessageSequence();
// 写入请求码和参数
data.writeInt(REQUEST_CODE_UNINSTALL_APP);
data.writeString(bundleName);
await proxy.sendMessageRequest(0, data, reply, option);
const success = reply.readInt();
return success === 1;
} catch (err) {
console.error(this.tag, `卸载应用失败: ${JSON.stringify(err)}`);
this.proxy = null; // 清除可能失效的代理
return false;
}
}
// 通过SA代理写入系统设置(system_core权限操作)
async writeSystemSetting(key: string, value: string): Promise<boolean> {
const proxy = await this.getProxy();
if (!proxy) {
return false;
}
try {
const option = new rpc.MessageOption();
const data = new rpc.MessageSequence();
const reply = new rpc.MessageSequence();
data.writeInt(REQUEST_CODE_WRITE_SETTINGS);
data.writeString(key);
data.writeString(value);
await proxy.sendMessageRequest(0, data, reply, option);
const success = reply.readInt();
return success === 1;
} catch (err) {
console.error(this.tag, `写入设置失败: ${JSON.stringify(err)}`);
this.proxy = null;
return false;
}
}
// 通过SA代理重启设备(system_core权限操作)
async rebootDevice(reason: string): Promise<boolean> {
const proxy = await this.getProxy();
if (!proxy) {
return false;
}
try {
const option = new rpc.MessageOption();
const data = new rpc.MessageSequence();
const reply = new rpc.MessageSequence();
data.writeInt(REQUEST_CODE_REBOOT);
data.writeString(reason);
await proxy.sendMessageRequest(0, data, reply, option);
return true; // 如果能收到回复说明重启还没执行
} catch (err) {
// 重启可能导致IPC断开,这其实是正常行为
console.info(this.tag, '设备正在重启...');
return true;
}
}
}
export default new CorePermissionProxy();
完整示例:权限审计面板
// pages/PermissionAuditPage.ets - 权限审计面板
import PermissionManager from '../common/PermissionManager';
import { Permissions } from '@ohos.abilityAccessCtrl';
import promptAction from '@ohos.promptAction';
interface PermissionInfo {
name: string;
level: string;
description: string;
}
@Entry
@Component
struct PermissionAuditPage {
@State permissionResults: Map<string, boolean> = new Map();
@State isAuditing: boolean = false;
// 需要审计的权限列表
private auditPermissions: PermissionInfo[] = [
{ name: 'ohos.permission.SET_TIME', level: 'system', description: '设置系统时间' },
{ name: 'ohos.permission.SET_TIME_ZONE', level: 'system', description: '设置时区' },
{ name: 'ohos.permission.INSTALL_BUNDLE', level: 'system', description: '安装应用' },
{ name: 'ohos.permission.GET_BUNDLE_INFO_PRIVILEGED', level: 'system', description: '获取应用详情' },
{ name: 'ohos.permission.UNINSTALL_BUNDLE', level: 'system_core', description: '卸载应用' },
{ name: 'ohos.permission.WRITE_SYSTEM_SETTINGS', level: 'system_core', description: '写入系统设置' },
{ name: 'ohos.permission.FACTORY_RESET', level: 'system_core', description: '恢复出厂设置' },
{ name: 'ohos.permission.REBOOT', level: 'system_core', description: '重启设备' },
];
async aboutToAppear() {
await this.runAudit();
}
// 执行权限审计
async runAudit() {
this.isAuditing = true;
this.permissionResults = new Map();
for (const perm of this.auditPermissions) {
const granted = await PermissionManager.checkPermission(perm.name as Permissions);
this.permissionResults.set(perm.name, granted);
}
this.isAuditing = false;
}
// 统计权限通过率
getPassRate(): string {
let total = 0;
let passed = 0;
this.permissionResults.forEach((granted, _) => {
total++;
if (granted) passed++;
});
return total === 0 ? '0%' : `${Math.round(passed / total * 100)}%`;
}
// 按级别统计
getLevelStats(level: string): { total: number; passed: number } {
let total = 0;
let passed = 0;
this.auditPermissions
.filter(p => p.level === level)
.forEach(p => {
total++;
if (this.permissionResults.get(p.name)) passed++;
});
return { total, passed };
}
build() {
Scroll() {
Column() {
// 审计概览
Row() {
Column() {
Text('权限通过率')
.fontSize(12)
.fontColor('#999999')
Text(this.getPassRate())
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor('#1976d2')
}
.alignItems(HorizontalAlign.Center)
Divider()
.vertical(true)
.height(40)
.color('#e0e0e0')
.margin({ left: 24, right: 24 })
Column() {
const sysStats = this.getLevelStats('system');
Text('system权限')
.fontSize(12)
.fontColor('#999999')
Text(`${sysStats.passed}/${sysStats.total}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('#ff9800')
}
.alignItems(HorizontalAlign.Center)
Divider()
.vertical(true)
.height(40)
.color('#e0e0e0')
.margin({ left: 24, right: 24 })
Column() {
const coreStats = this.getLevelStats('system_core');
Text('system_core权限')
.fontSize(12)
.fontColor('#999999')
Text(`${coreStats.passed}/${coreStats.total}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('#f44336')
}
.alignItems(HorizontalAlign.Center)
}
.width('100%')
.justifyContent(FlexAlign.Center)
.padding(20)
.backgroundColor('#ffffff')
.borderRadius(12)
.margin({ bottom: 16 })
// system级别权限
Column() {
Text('system级别权限')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 12 })
ForEach(
this.auditPermissions.filter(p => p.level === 'system'),
(perm: PermissionInfo) => {
this.PermissionRow(perm)
},
(perm: PermissionInfo) => perm.name
)
}
.width('100%')
.padding(16)
.backgroundColor('#ffffff')
.borderRadius(12)
.margin({ bottom: 16 })
// system_core级别权限
Column() {
Text('system_core级别权限')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 12 })
ForEach(
this.auditPermissions.filter(p => p.level === 'system_core'),
(perm: PermissionInfo) => {
this.PermissionRow(perm)
},
(perm: PermissionInfo) => perm.name
)
}
.width('100%')
.padding(16)
.backgroundColor('#ffffff')
.borderRadius(12)
.margin({ bottom: 16 })
// 操作按钮
Button('重新审计')
.width('100%')
.height(48)
.backgroundColor('#1976d2')
.borderRadius(8)
.enabled(!this.isAuditing)
.onClick(() => this.runAudit())
}
.padding(24)
}
.width('100%')
.height('100%')
.backgroundColor('#f5f5f5')
}
@Builder
PermissionRow(perm: PermissionInfo) {
Row() {
Column() {
Text(perm.description)
.fontSize(14)
.fontColor('#333333')
Text(perm.name)
.fontSize(11)
.fontColor('#999999')
.margin({ top: 2 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Text(this.permissionResults.get(perm.name) ? '已授权' : '未授权')
.fontSize(13)
.fontColor(this.permissionResults.get(perm.name) ? '#4caf50' : '#f44336')
.padding({ left: 8, right: 8, top: 4, bottom: 4 })
.backgroundColor(this.permissionResults.get(perm.name) ? '#e8f5e9' : '#fce4ec')
.borderRadius(4)
}
.width('100%')
.margin({ bottom: 10 })
}
}
踩坑与注意事项
坑一:权限声明了但签名级别不够
这是最常见的问题。你在module.json5里声明了ohos.permission.UNINSTALL_BUNDLE(system_core级别),但你的应用只有系统签名(不够platform签名),安装时这个权限直接被忽略,调用API时报Permission denied。
而且这个报错信息不会告诉你"签名级别不够",只会说"Permission denied"。你得自己查权限级别表,判断是签名不够还是声明遗漏。
坑二:混淆system和system_core
有些权限的级别在不同HarmonyOS版本之间发生了变化。比如INSTALL_BUNDLE在早期版本是system_core级别,后来降级为system级别。如果你的代码是基于旧文档写的,可能判断有误。
务必以当前版本的权限定义文件为准,不要依赖过时的文档。
坑三:权限审计遗漏
你的应用声明了20个权限,但实际只用了15个。多出来的5个权限是安全风险——万一有漏洞被利用,攻击者能通过这些多余权限做更多事。
定期做权限审计,把不需要的权限删掉。原则是:最小权限原则,只声明必须的权限。
坑四:IPC代理的安全性
通过SA代理调用system_core权限的操作时,SA本身不做调用者校验——任何能拿到SA代理的进程都能调用。这意味着如果你的SA暴露了卸载应用的接口,恶意应用也能通过这个接口卸载其他应用。
解决办法:在SA里校验调用者的身份:
// 在SA的OnRemoteRequest中校验调用者
OnRemoteRequest(code: number, data: rpc.MessageSequence, reply: rpc.MessageSequence,
option: rpc.MessageOption): boolean {
// 校验调用者是否为受信任的应用
const callerTokenId = rpc.getCallingTokenId();
if (!this.isTrustedCaller(callerTokenId)) {
console.error(this.tag, `不受信任的调用者: ${callerTokenId}`);
reply.writeInt(0); // 返回失败
return true;
}
// 校验通过,执行操作
switch (code) {
case REQUEST_CODE_UNINSTALL_APP:
// 执行卸载操作
break;
}
return true;
}
// 检查调用者是否受信任
isTrustedCaller(tokenId: number): boolean {
// 检查调用者的包名是否在白名单中
// 或者检查调用者是否有系统签名
return true; // 简化示例
}
坑五:权限声明格式错误
module.json5里的权限声明格式必须严格正确。常见错误:
// 错误:权限名拼写错误
{ "name": "ohos.permission.set_time" } // 小写,应该是大写
// 错误:权限对象缺少必要字段
{ "name": "ohos.permission.SET_TIME" } // 缺少reason和usedScene
// 正确:完整的权限声明
{
"name": "ohos.permission.SET_TIME",
"reason": "$string:permission_reason",
"usedScene": {
"abilities": ["MainAbility"],
"when": "inuse"
}
}
HarmonyOS 6适配说明
HarmonyOS 6在权限体系方面有几个重要变化:
-
权限级别新增restricted:在
system和system_core之间新增了restricted级别,用于特别敏感的操作(如生物识别、位置精确定位)。restricted权限需要系统签名+用户确认。 -
权限声明必须包含reason:所有
system及以上级别的权限声明必须包含reason字段和usedScene字段,否则安装时拒绝。之前这个只是推荐,现在变成强制。 -
权限使用追踪:系统会记录每个权限的使用时间和频率。如果某个权限长时间未使用,系统会提示用户回收。这对系统应用也适用。
-
权限沙箱增强:即使有
system_core权限,系统应用也不能随意访问其他应用的数据。需要通过DataShare接口按规则访问。
适配建议:检查所有权限声明是否包含reason和usedScene,清理不必要的权限声明,新增的restricted权限按需申请。
总结
权限分级是鸿蒙安全体系的基石。normal、system、system_core三级权限对应不同的签名门槛,搞不清分级就写不对权限声明。
核心要点回顾:
system权限需要系统签名,system_core权限需要平台签名- 权限声明和签名必须匹配,缺一不可
- 不够签名级别时,通过SA代理或拆分应用实现权限提升
- IPC代理要做调用者校验,防止权限泄露
- 定期做权限审计,遵循最小权限原则
| 维度 | 评价 |
|---|---|
| 学习难度 | ⭐⭐⭐⭐ 权限分级体系不复杂,但权限列表多、版本变化多 |
| 使用频率 | ⭐⭐⭐⭐⭐ 做系统级开发必用,每个系统应用都要处理权限 |
| 重要程度 | ⭐⭐⭐⭐⭐ 安全的核心,权限管理出错可能导致安全漏洞 |
- 点赞
- 收藏
- 关注作者
评论(0)