zoukankan      html  css  js  c++  java
  • 从零开始系统深入学习android(实践让我们开始写代码Android框架学习6.权限(Permissions))

    第6章 权限(Permissions)

    本文档介绍了应用程序开发人员如何使用由Android提供的安全功能。在Android 开放源代码项目AOSP(Android Open Source Project)中提供了更普通的Android安全性概述。Android是一种分权限的操作系统,在Android上运行的每个应用程序都具有各自独立的系统标识(Linux用户ID和组ID)。系统各部分有不同并明显的标识。因此,Linux上运行的各个应用程序相互独立且与系统无关。

    Android的“permissions”机制通俗来说就是你程序就算实现了那个功能,如果没申请权限的话,那个功能一样运行部了。并且每个应用程序的权限都是独立的。

    6.1 安全性框架

    Android安全体系架构设计的核心是在默认情况下不允许任何一个程序可以执行对其他程序、操作系统或者用户有害的操作,包括读写用户的隐私数据(例如联系人或者电子邮件),读写其他程序的文件,进行网络访问或者唤醒设备等等。由于Android系统让每个应用程序运行在独立的沙箱(sandboxes)中,应用程序必须通过声明所需要的权限来明确的分配资源和数据。Android没有使用不利于安全的动态授权机制。应用程序静态的声明他们所需要的权限,在程序安装时Android系统会提示用户是否同意它们获取这些权限。不同意的话就不要安装,安装了即同意。Dalvik虚拟机不是一个安全的边界,任何的应用程序都能够运行本地代码(参照Android NDK)。所有类型的应用程序——java、native和两者混合的——均用相同方式和相同程度的安全等级在沙盒中运行。

    6.2 应用程序数字签名

    所有的Android应用程序(apk文件)都必须让某个开发人员持有私钥、用于识别应用程序作者的证书进行签名。该证书要求很宽松,并不需要由专门的证书颁发机构进行签名,Android应用程序可以使用自签名的证书。Android证书的目的是区分应用程序的作者,可以允许操作系统授予或者拒绝应用程序使用签名级别的权限和操作系统授予或者拒绝应用程序请求和其他应用程序相同的Linux身份。关于这一点,在调试阶段的时候,如果你有在两台电脑分别编译程序,生成2个相同APK后,装入手机的话,后面那个会提示“安装未完成”或非法之类。这是由于你两台电脑的调式模式下的签名不一样,在正式发布的时候,使用Eclipise可以给程序正式签名,在不同的电脑上发布只要使用相同的正式签名即可。当然调试阶段不用这么麻烦。

    6.3 用户ID和文件访问

    在安装的时候,Android会给每个程序分配一个不同的Linux用户IU(UID)。软件在设备上的生命周期中这个身份标识保持不变。在不同的设备上,相同的软件可能会有一个不同的UID;重要的是在给定的设备上不同的包是不同的UID。因为安全是在进程级别上实现的,理论上,两个软件包的代码在同一个进程中不能够同时正常运行,他们必须以不同的Linux用户运行。实际上,可以在每个程序的AndroidManifest.xml中将manifest标签的shareUserId属性分配相同的用户ID,把两个应用程序看作拥有同样的用户ID和文件权限的同一个应用程序。为了保持安全,只有具有相同的数字签名(请求的sharedUserId也相同)的应用程序才会分配相同的用户ID。任何由应用程序存储的数据将被赋予应用程序的用户ID,正常情况不能被其它应用程序访问。当使用getSharedPreferences(String, int),openFileOutput(String,int),或openOrCreateDatabase(String,int, SQLiteDatabase.CursorFactory)创建一个新的文件时,可以使用MODE_WORLD_READABLE或MODE_WORLD_WRITEABLE标记允许其他应用程序来读/写文件。设置这些全局的读写权限后,该文件仍然由创建改文件的应用程序所拥有,但任何其他应用程序可以看到读写它。

    6.4 使用权限

    一个Android应用程序没有任何权限,这意味着它不能做任何会对用户体验或设备上的任何数据造成不利影响的操作。要利用设备的保护功能,必须在AndroidManifest.xml文件中的一个或多个<uses-permission>标签上声明应用程序所需要的权限。例如,一个监控接收短信的应用程序需要声明如下权限,如代码清单6-1所示:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.android.app.myapp" >
        <uses-permission android:name="android.permission.RECEIVE_SMS" />
        ...
    </manifest>

    代码清单 6-1

    在安装应用程序时,安装者要基于应用程序的签名在交互时对应用程序所需的权限进行授权。应用程序运行时不会进行权限检查:它要么在安装后被给予一个特殊的权限,并且可以使用它期望的权限,要么就不被授予权限,任何使用这些权限的操作都会在没有用户提示的情况下自动失败。通常如果请求权限失败应用程序会抛出一个SecurityException异常,但是也有特例。例如,sendBroadcast(Intent)函数在所有数据被投递到接收者时检查权限,当函数返回后,不会对数据的权限进行检查,也不能接收到任何权限异常。约大多数情况下,权限异常会记录在日志中。所有Android系统提供的权限可以在Manifest.permission中找到。任何应用程序也可以定义并执行其自己的权限,

    6.5 声明和实施权限

    为了执行你自己的权限,你必须首先在你的AndroidManifest.xml中使用一个或多个<permission>标签声明它们。例如,一个应用程序想要控制谁能够启动它的一个Activity,可以这样声明,如代码清单6-2所示:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.me.app.myapp" >
        <permission android:name="com.me.app.myapp.permission.DEADLY_ACTIVITY"
            android:label="@string/permlab_deadlyActivity"
            android:description="@string/permdesc_deadlyActivity"
            android:permissionGroup="android.permission-group.COST_MONEY"
            android:protectionLevel="dangerous" />
        ...
    </manifest>

    代码清单 6-2

    <protectionLevel>属性是必选的,它告诉系统当其它应用程序需要该权限时怎样通知用户或者谁可以使用该权限。<permissionGroup>属性是可选的,用于帮助系统显示权限给用户。通常会将它设置为在android.Manifest.permission_group中的一个标准的系统组,在极少数情况下也可以由自己进行定义。最好是优先使用现有的组,便于简化权限UI展示给用户。请注意,这两个标签和描述应提供许可。用户在查看的权限列表(android:label)或单个权限( android:description)的细节时,这些内容会被显示。标签应该简洁的介绍权限保护的关键功能。用几个简单的句子描述拥有该权限可以做什么。惯例是用两个句子,第一句描述权限,第二句警告用户当授权该权限后会发生什么。

    你也可以通过shell命令 adb shell pm list permissions来查看现在系统上的权限定义。特别地,-s选项可以用简单的表格形式来显示。如代码清单6-3所示:

    $ adb shell pm list permissions -s
    All Permissions:
     
    Network communication: view Wi-Fi state, create Bluetooth connections, full
    Internet access, view network state
     
    Your location: access extra location provider commands, fine (GPS) location,
    mock location sources for testing, coarse (network-based) location
     
    Services that cost you money: send SMS messages, directly call phone numbers
     
    ...

    代码清单 6-3

    1. 在AndroidManifest.xml中执行权限

    进入系统或应用程序的组件的高级别权限可以在AndroidManifest.xml中实现。所有这些都可以在android:permission 属性中使用。不仅仅应用程序可以使用权限,我们也可以为某个组件设置权限

    ◆Activity权限(应用于<activity>标签)用于限制谁才可以启动相关的Activity。该权限在运行Context.startActivity()和Activity.startActivityForResult()期间被检查;如果调用方不具有相应必需的权限,那么将会从此次调用中抛出SecurityException异常。

    ◆Service权限(应用于<service>标签)用于限制谁才可以启动或绑定该service。在运行Context.startService(),Context.stopService()和Context.bindService()调用的时候会进行权限检查。如果调用方没有所需的权限,则会抛出一个SecurityException异常。

    ◆BroadcastReceiver权限(应用于<receiver>标签)用于限制谁可以向相关的接收器发送广播。权限检查会在Context.sendBroadcast()返回后当系统去发送已经提交的广播给相应的Receiver时进行。最终,一个permission failure不会再返回给调用方一个异常,只是不会去实现该Intent而已。同样地,Context.registerReceiver()也可以通过自己的permission用于限制谁可以向一个在程序中注册的receiver发送广播。另一种方式是,一个permission也可以提供给Context.sendBroadcast() 用以限制哪一个BroadcastReceiver才可以接收该广播。

    ◆ContentProvider权限(应用于<provider>标签)用于限制谁才可以访问ContentProvider提供的数据。(Content providers有一套额外的安全机制叫做URI permissions,这些在稍后讨论。)不像其他组件,它有两个单独的权限属性,你可以设置:android:readPermission用于限制谁能够读,android:writePermission用于限制谁能够写。需要注意的是如果provider同时需要读写许可,只有写许可的情况下并不能读取provider中的数据。当你第一次检索Content Provider和当完成相关操作时会进行权限检查。(假如没有任何权限则会抛出SecurityException异常。)使用ContentResolver.query()需要持有读权限;使用ContentResolver.insert(),ContentResolver.update(),ContentResolver.delete()需要写权限。在所有这些情况下,没有所需的权限将会导致抛出SecurityException异常。

    2. 发送广播执行权限

    除了之前说过的权限(用于限制谁可以发送广播给相应的BroadcastReceiver),还可以在发送广播的时候指定一个权限。在调用Context.sendBroadcast()的时候使用一个权限字符串(permission string),你就可以要求接收的宿主程序必须有相应的权限。值得注意的是接收者和广播都可以要求权限。当这种情况发生时,这两种权限检查都需要通过后才会将相应的intent发送给相关的目标。

    3. 执行其他权限

    在调用service的过程中可以设置更加细化的权限。这是通过Context.checkCallingPermission()方法来完成的。调用的时候使用一个想得到的权限字符串(permission string),返回给调用方一个整数判断是否具有相关权限。需要注意的是这种情况只能发生在来自另一个进程的调用,通常是一个service发布的IDL接口或者是其他方式提供给其他的进程。Android提供了很多其他有效的方法用于检查权限。如果有另一个进程的pid,可以通过Context.checkPermission(String, int, int)去检查该进程的权限设置。如果有另一个应用程序的包名,可以直接用PackageManager.checkPermission(String, String)来确定该包是否已经拥有了相应的权限。

    6.6 URI权限

    到目前为止我们讨论的标准的权限系统对于content provider来说是不够的。一个Content Provider可能想保护它的读写权限,而同时与它对应的客户端也需要将特定的URI传递给其它应用程序以便对该URI进行操作。一个典型的例子是邮件应用程序的附件。访问邮件需要使用permission来保护,因为这些是敏感的用户数据。然而,如果有一个指向图片附件的URI需要传递给图片浏览器,那个图片浏览器是不会有访问附件的权利的,因为它不可能拥有所有的邮件的访问权限。针对这个问题的解决方案就是per-URI 权限:当启动一个activity或者给一个activity返回结果的时候,调用方可以设置Intent.FLAG_GRANT_READ_URI_PERMISSION和/或 Intent.FLAG_GRANT_WRITE_URI_PERMISSION。这授予接收activity访问该Intent指定的URI的权限,而不管它是否有权限进入该Intent对应的content provider。这种机制允许一个通常的(capability-style)模型,以让用户交互(如打开一个附件, 从列表中选择一个联系人)并细化的权限。这是实现减少应用程序所需要的权限而只留下和程序行为直接相关的权限时很关键的一步。强烈建议在content provider中实现这种功能,并通过android:grantUriPermissions或者<grant-uri-permissions>标签来声明支持。

     FAQ群213821767

  • 相关阅读:
    四则运算WEB版
    最大子数组问题
    四则运算终极版
    软件工程个人作业02
    软件工程概论-构建之法阅读笔记01
    软件工程概论个人作业01
    软件工程概论作业-测试
    123
    【好文转载】凡人修真传-程序员的十个等级
    有趣的网站
  • 原文地址:https://www.cnblogs.com/tianjian/p/2697039.html
Copyright © 2011-2022 走看看