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 权限保护级别详解:别把「公开」当「签名」

保护级别是自定义权限的灵魂。它决定了系统如何授予这个权限。我见过太多开发者把 normaldangerous 搞混,结果隐私数据裸奔。

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交互的完整链路。

自定义权限知识体系 1. 声明权限 <permission> 标签 2. 保护级别 normal/dangerous/signature 3. 组件引用 Activity/Service等 Intent 权限交互 sendBroadcast(..., permission) PendingIntent + FLAG_IMMUTABLE 签名验证机制 signature 级别自动授权 同签名App互信 核心原则 内部接口用 signature,外部接口用 dangerous

5.5 实战建议:自定义权限的最佳实践

最后,我总结几条实战经验,都是踩过坑换来的:

  1. 权限名要唯一。用包名前缀,避免冲突。我见过两个App都叫 READ_DATA,结果互相覆盖,权限检查全乱套。
  2. 保护级别选对。内部模块用 signature,对外接口用 dangerous。别偷懒全用 normal
  3. Intent权限别忘。发送广播或PendingIntent时,显式指定权限参数。你想想看,如果不指定,那广播就是裸奔的。
  4. 文档写清楚。自定义权限的用途、级别、依赖关系,都要写进开发文档。我团队里有个规矩:没文档的权限,不允许上线。
一句话总结:自定义权限是Android安全模型的「自定义规则」。用好了,你的App安全边界固若金汤;用错了,那就是给攻击者留后门。
公众号:蓝海资料掘金营,微信deep3321