5. Android权限模型(三):自定义权限、权限保护级别详解、权限与Intent交互
好,我们继续聊权限模型。前两章我们把系统预置权限和运行时权限讲透了。这一章,我来聊聊「自定义权限」——说白了,就是你自己写的App,也能定义自己的权限规则。
很多人觉得自定义权限是高级功能,用不上。其实不然。我做过一个企业级MDM(移动设备管理)项目,里面十几个模块互相调用,如果没有自定义权限做隔离,那安全边界就是一纸空谈。嗯,这里要注意,自定义权限用好了是铠甲,用不好就是摆设。
5.1 自定义权限:让你的App也能「发号施令」
自定义权限,就是开发者自己声明的权限。其他App想访问你的组件(Activity、Service、ContentProvider、BroadcastReceiver),必须先申请这个权限。
声明方式很简单,在 AndroidManifest.xml 里加一个 <permission> 标签:
<permission
android:name="com.example.myapp.permission.MY_SECURE_ACTION"
android:label="我的安全操作"
android:description="允许执行我的安全操作"
android:protectionLevel="signature" />
然后,在组件上引用它:
<activity
android:name=".SecureActivity"
android:permission="com.example.myapp.permission.MY_SECURE_ACTION">
...
</activity>
这样,只有持有该权限的App才能启动 SecureActivity。
com.example.myapp.permission.READ_DATA。别乱起名,否则后期维护想哭。
5.2 权限保护级别详解:别把「公开」当「签名」
保护级别是自定义权限的灵魂。它决定了系统如何授予这个权限。我见过太多开发者把 normal 和 dangerous 搞混,结果隐私数据裸奔。
Android 定义了四个核心保护级别:
| 保护级别 | 授予方式 | 典型场景 |
|---|---|---|
normal |
安装时自动授予 | 不涉及隐私的轻量操作 |
dangerous |
运行时弹窗申请 | 访问通讯录、位置等 |
signature |
仅同签名App自动获得 | 自家App之间的安全通信 |
signatureOrSystem |
系统应用或同签名App | 系统级功能扩展 |
这里我重点讲 signature 级别。为什么?因为它是「自己人」的通行证。
我曾经在一个金融项目里,用 signature 级别保护了核心支付模块。只有我们公司签名的App才能调用。第三方恶意App就算知道权限名,也拿不到授权——因为签名不匹配。这就是「签名即身份」的威力。
normal。结果任何App都能声明这个权限,然后随意调用。嗯,那场面……数据泄露得干干净净。记住:内部接口,一律用 signature。
5.3 权限与Intent交互:隐式意图的「安检门」
权限不光管组件调用,还管Intent的发送和接收。这里有两个关键点:
- 发送方权限:你发Intent时,可以要求接收方必须持有某个权限。
- 接收方权限:你的组件声明了权限,只有持有该权限的发送方才能启动它。
举个例子,发送带权限的广播:
Intent intent = new Intent("com.example.myapp.ACTION_SECURE");
intent.setPackage("com.example.receiver");
sendBroadcast(intent, "com.example.myapp.permission.RECEIVE_SECURE");
这样,只有声明了 RECEIVE_SECURE 权限的接收器才能收到这条广播。我当年做插件化框架时,就用这招隔离不同插件之间的通信,效果很好。
再来看PendingIntent。它本身可以携带权限,跨进程传递时依然有效。比如:
Intent intent = new Intent(this, TargetActivity.class);
PendingIntent pi = PendingIntent.getActivity(
this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
// 把这个 pi 传给其他进程,其他进程可以用它启动 TargetActivity
// 但权限检查依然基于你App的身份
这里有个坑:FLAG_IMMUTABLE 是Android 12之后推荐的。为什么?因为可变PendingIntent容易被恶意篡改。我建议你一律用不可变,除非你明确知道自己在做什么。
5.4 知识体系总览
下面这张图,把本章的核心逻辑串起来了。你可以看到自定义权限从声明、保护级别到Intent交互的完整链路。
5.5 实战建议:自定义权限的最佳实践
最后,我总结几条实战经验,都是踩过坑换来的:
- 权限名要唯一。用包名前缀,避免冲突。我见过两个App都叫
READ_DATA,结果互相覆盖,权限检查全乱套。 - 保护级别选对。内部模块用
signature,对外接口用dangerous。别偷懒全用normal。 - Intent权限别忘。发送广播或PendingIntent时,显式指定权限参数。你想想看,如果不指定,那广播就是裸奔的。
- 文档写清楚。自定义权限的用途、级别、依赖关系,都要写进开发文档。我团队里有个规矩:没文档的权限,不允许上线。