权限管理:Android权限模型演进、最小权限原则、运行时权限处理、自定义权限
聊到Android安全,权限管理绝对是个绕不开的话题。我做了这么多年安全开发,见过太多因为权限处理不当引发的安全事故。说白了,权限就是应用和系统之间的一道门——门开多大,什么时候开,开完要不要关上,这些都得想清楚。
Android权限模型的演进
Android的权限模型不是一天建成的。它经历了几个重要阶段,每个阶段都是为了解决上一阶段的问题。
| 版本 | 权限模型 | 核心特点 |
|---|---|---|
| Android 1.0 - 5.1 | 安装时授权 | 安装时一次性授予所有权限,无法撤销 |
| Android 6.0 - 9.0 | 运行时权限 | 用户可在使用中授权或拒绝,可随时撤销 |
| Android 10+ | 受限权限 + 分区存储 | 后台位置、文件访问等进一步收紧 |
| Android 11+ | 自动重置权限 | 长时间未使用的应用,权限自动重置 |
我记得刚入行那会儿,Android 4.4 还是主流。那时候权限管理简单粗暴——用户要么全给,要么不装。很多应用趁机要了一堆跟功能无关的权限。比如一个手电筒应用,居然要读取联系人权限。这合理吗?显然不合理。
为什么会这样?因为早期开发者习惯了「想要什么就拿什么」的思维。直到 Android 6.0 推出运行时权限模型,整个行业才被迫改变。
最小权限原则
最小权限原则,说白了就是「够用就好」。你的应用只需要完成功能所必需的最小权限集合,多一个都不要。
核心思想:每个模块只拥有完成其任务所必需的最小权限,不多不少。
我在项目中遇到过这样一个案例:一个图片编辑应用,申请了 CAMERA、READ_EXTERNAL_STORAGE、WRITE_EXTERNAL_STORAGE、ACCESS_FINE_LOCATION 四个权限。但仔细一分析,定位权限根本用不上。为什么还要申请?产品经理说「以后可能要用」。嗯,这种「以防万一」的心态,恰恰是安全漏洞的温床。
如何落实最小权限原则?我建议从这几个方面入手:
- 逐功能审查:每个功能模块单独列出需要的权限,去掉冗余项
- 权限分组:将权限按功能模块划分,避免一个模块的权限被另一个模块滥用
- 动态检查:运行时检查权限是否被撤销,而不是假设权限一直有效
- 权限降级:如果某个功能不需要精确权限,就用模糊权限替代
我的习惯:每次提交代码前,我都会打开 AndroidManifest.xml 看一眼,问自己三个问题——这个权限真的需要吗?有没有更低权限的方案?用户会不会觉得这个权限很奇怪?
运行时权限处理
运行时权限是 Android 6.0 引入的核心机制。它把授权时机从「安装时」移到了「使用时」。用户可以在使用某个功能时决定是否授权,也可以随时去设置里撤销。
处理运行时权限,我总结了一套标准流程:
- 检查权限:调用
ContextCompat.checkSelfPermission()检查当前状态 - 解释原因:如果用户之前拒绝过,用
shouldShowRequestPermissionRationale()判断是否需要解释 - 发起请求:调用
ActivityCompat.requestPermissions()弹出系统对话框 - 处理回调:在
onRequestPermissionsResult()中处理用户的决定 - 处理拒绝:如果用户拒绝且勾选「不再询问」,引导用户去设置页面手动开启
// 一个标准的运行时权限请求示例
private void requestCameraPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
// 用户之前拒绝过,需要解释
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.CAMERA)) {
// 弹出自定义对话框解释为什么需要相机权限
showPermissionExplanationDialog();
} else {
// 直接请求权限
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CAMERA},
REQUEST_CAMERA_PERMISSION);
}
} else {
// 权限已授予,直接打开相机
openCamera();
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CAMERA_PERMISSION) {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
openCamera();
} else {
// 用户拒绝了权限
showPermissionDeniedMessage();
}
}
}
注意:千万不要在 onCreate() 里一股脑请求所有权限。用户刚打开应用就被一堆权限弹窗轰炸,大概率会直接卸载。我曾经见过一个应用,启动时连续弹出 5 个权限请求,用户连界面都没看到就被劝退了。
自定义权限
有时候,系统提供的权限不够用。比如你的应用需要暴露一个组件给其他应用调用,但又不想让所有应用都能调用。这时候就需要自定义权限。
自定义权限的定义很简单,在 AndroidManifest.xml 中声明即可:
<permission
android:name="com.example.myapp.permission.MY_CUSTOM_PERMISSION"
android:label="我的自定义权限"
android:description="用于访问我的应用的敏感功能"
android:protectionLevel="dangerous" />
这里有个关键点——protectionLevel。它决定了权限的保护级别:
| 保护级别 | 说明 | 使用场景 |
|---|---|---|
| normal | 低风险,系统自动授予 | 不涉及用户隐私的功能 |
| dangerous | 高风险,需要用户确认 | 涉及用户隐私或敏感数据 |
| signature | 仅同签名应用可获取 | 自家应用之间的通信 |
| signatureOrSystem | 系统应用或同签名应用 | 系统级功能 |
我个人最常用的是 signature 级别。为什么?因为它不需要用户交互,而且只有跟你签名一致的应用才能访问。说白了,这就是「自己人」的通行证。
使用自定义权限时,调用方需要在 AndroidManifest.xml 中声明 <uses-permission>:
<uses-permission android:name="com.example.myapp.permission.MY_CUSTOM_PERMISSION" />
而被调用的组件(比如 Activity 或 Service)需要设置 android:permission 属性:
<activity
android:name=".SensitiveActivity"
android:permission="com.example.myapp.permission.MY_CUSTOM_PERMISSION">
<intent-filter>
<action android:name="com.example.myapp.action.SENSITIVE_ACTION" />
</intent-filter>
</activity>
避坑指南:我曾经犯过一个错误——自定义权限的 protectionLevel 设成了 normal,结果任何应用都能调用我的敏感组件。后来我改成 signature 才解决问题。记住,自定义权限的级别一定要跟实际风险匹配。
知识体系总览
下面这张图梳理了本章的核心知识点,方便你快速回顾:
权限管理这件事,说难不难,说简单也不简单。关键在于养成习惯——每次加权限前多问一句「真的需要吗」,每次处理权限请求时多想一步「用户会怎么想」。你想想看,如果每个开发者都这么做,Android 生态的安全水平会提升一大截。
公众号:蓝海资料掘金营,微信deep3321