0000

这个阶段搞了很多和Android文件权限相关的问题,虽然一知半解,但也算是对Android权限机制有一些自己的理解。遂将这些内容整理出来。因为权限这部分涉及到的内容很多,故将知识分为几块内容分别去整理。目前能想到的概要如下:

  1. Android 权限底层实现原理概述
  2. Android uid,gid的生成与权限机制的联系
  3. Android packageManagerService与权限的千丝万缕(源码解析)
  4. Android 从recovery模式下的OTA升级理解权限
  5. Android ROOT 原理
  6. Android 签名
  7. Android 权限大杀器 — Selinux的策略

这是第一篇。2,3,4已经有一些草稿了,但离发出来还有一些时间。5,6,7还在规划中。可以关注新弄的公众号softard,后面比较完善的文章都会在这个号上及时更新。

这里再推荐一本书,这也是查资料找到的一本宝贝,非常牛逼。

0001 Overview

Linux File Permission

众所周知,Android的内核是linux的内核。对于linux来说,系统的一切都是文件。同时,linux为文件系统设计了一套完善的权限机制。下面简单提一下linux文件权限的核心:

比如在Android手机adb shell下查看一个目录

# ls -ld data/data/
drwxrwx--x 113 system system 4096 1970-01-01 15:08 data/data/

其中,drwxrwx--x这10位代表文件权限,第一位文件类型可以忽略(这里类型是文件夹),后面每三位代表文件拥有的权限,包括rwx(可读,可写,可执行)。system system表示文件属于system用户和属于system组。

翻译过来是,system用户对该文件拥有读写执行权限,system组对该文件拥有读写执行权限,其他用户对该文件拥有执行权限。简单来说就是771权限。

Android Permission

Permission权限是Android系统定义的一套权限机制,用于控制APP访问某个硬件设备或某个Android系统的组件。

举两个常见的例子:

  1. 如果你的App想要访问存储卡,你需要在你的AndroidManifest文件中使用对应的permission用于向系统请求权限 。
  2. 你可以给你的Activity组件加个自定义的访问权限,这样任何想启动该Activity的程序必须在它的AndroidManifest中进行权限的请求。见Android自定义权限

那么为什么你在AndroidManifest文件请求storage权限你就可以访问设备文件?linux文件属性的权限和Permission到底是怎么联系起来的呢?下面我们来具体来讲。

0010 packages.list & packages.xml

Android开机阶段会扫描所有App,从Manifest文件中把App信息和权限存到packages.xmlpackages.list文件中,具体的处理过程会在后面第三篇去分析。

因为文件包含所有已安装应用的信息,所以我们尝试安装一个App(com.softard.test),并且查看其信息:

packages.list

com.softard.test 10052 1 /data/user/0/com.softard.test default none

这里10052是uid,至于其怎么生成的后面第二篇再详谈。

packages.xml

<package name="com.softard.test" codePath="/data/app/com.softard.test-1" nativeLibraryPath="/data/app/com.softard.test-1/lib" publicFlags="944291654" privateFlags="0" ft="8bbd70" it="89023c" ut="8bc04d" version="1" userId="10052">
<sigs count="1">
<cert index="10" key="308..../>
</sigs>
<proper-signing-keyset identifier="11" />
</package>

然后对App做个修改,将其改成系统App并签名放到system/app/Test下,再看这两个文件:

com.softard.test 1000 0 /data/user/0/com.softard.test platform 3009,3002,1023,1015,3003,3001,1021,1000,2002,2950,1010,1007

看到uid变成了1000,selinux从default变成platform,权限组从none变成3009,3002,1023,1015,3003,3001,1021,1000,2002,2950,1010,1007。而且应用一下子增加了一堆权限:

 <package name="com.softard.test" codePath="/system/app/Test" nativeLibraryPath="/system/app/Test/lib" primaryCpuAbi="armeabi-v7a" publicFlags="944291397" privateFlags="0" ft="1683b80c790" it="1683b80c790" ut="1683b80c790" version="1" sharedUserId="1000" isOrphaned="true">
<sigs count="1">
<cert index="0" />
</sigs>
<perms>
<item name="android.permission.BIND_INCALL_SERVICE" granted="true" flags="0" />
<item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />
<item name="android.permission.CONFIGURE_WIFI_DISPLAY" granted="true" flags="0" />
<item name="android.permission.CONFIGURE_DISPLAY_COLOR_MODE" granted="true" flags="0" />
<item name="android.permission.ACCESS_WIMAX_STATE" granted="true" flags="0" />
<item name="android.permission.RECOVERY" granted="true" flags="0" />
<item name="com.qualcomm.permission.READPROC" granted="true" flags="0" />
<item name="android.permission.USE_CREDENTIALS" granted="true" flags="0" />
<item name="android.permission.MODIFY_AUDIO_SETTINGS" granted="true" flags="0" />
<item name="android.permission.ACCESS_CHECKIN_PROPERTIES" granted="true" flags="0" />
<item name="com.zqautomotive.oris.wechat.provider.WRITE" granted="true" flags="0" />
<item name="android.permission.INSTALL_LOCATION_PROVIDER" granted="true" flags="0" />
<item name="android.permission.MANAGE_ACCOUNTS" granted="true" flags="0" />
<item name="android.permission.SYSTEM_ALERT_WINDOW" granted="true" flags="0" />
<item name="android.permission.BROADCAST_PHONE_ACCOUNT_REGISTRATION" granted="true" flags="0" />
<item name="android.permission.CONTROL_LOCATION_UPDATES" granted="true" flags="0" />
<item name="android.permission.CLEAR_APP_USER_DATA" granted="true" flags="0" />
<item name="com.zqautomotive.oris.wechat.provider.READ" granted="true" flags="0" />
<item name="android.permission.BROADCAST_CALLLOG_INFO" granted="true" flags="0" />
<item name="android.permission.NFC" granted="true" flags="0" />
<item name="android.permission.CALL_PRIVILEGED" granted="true" flags="0" />
<item name="android.permission.CHANGE_NETWORK_STATE" granted="true" flags="0" />
<item name="android.permission.MASTER_CLEAR" granted="true" flags="0" />
<item name="android.permission.WRITE_SYNC_SETTINGS" granted="true" flags="0" />
<item name="android.permission.RECEIVE_BOOT_COMPLETED" granted="true" flags="0" />
<item name="android.permission.PEERS_MAC_ADDRESS" granted="true" flags="0" />
<item name="android.permission.DEVICE_POWER" granted="true" flags="0" />
<item name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" granted="true" flags="0" />
<item name="android.permission.READ_PROFILE" granted="true" flags="0" />
<item name="android.permission.BLUETOOTH" granted="true" flags="0" />
<item name="android.permission.WRITE_MEDIA_STORAGE" granted="true" flags="0" />
<item name="android.permission.WRITE_BLOCKED_NUMBERS" granted="true" flags="0" />
<item name="com.qualcomm.permission.USE_QCRIL_MSG_TUNNEL" granted="true" flags="0" />
<item name="android.permission.ACCESS_SURFACE_FLINGER" granted="true" flags="0" />
<item name="com.zqautomotive.voice_controller.GET_SPEECH_RESULT" granted="true" flags="0" />
<item name="android.permission.AUTHENTICATE_ACCOUNTS" granted="true" flags="0" />
<item name="android.permission.INTERNET" granted="true" flags="0" />
<item name="android.permission.REORDER_TASKS" granted="true" flags="0" />
<item name="zq.permissio.CONNECTION_MQTT_SERVICE" granted="true" flags="0" />
<item name="android.permission.BLUETOOTH_ADMIN" granted="true" flags="0" />
<item name="android.permission.CONTROL_VPN" granted="true" flags="0" />
<item name="android.permission.UPDATE_DEVICE_STATS" granted="true" flags="0" />
<item name="android.permission.MANAGE_FINGERPRINT" granted="true" flags="0" />
<item name="com.qualcomm.permission.IZAT" granted="true" flags="0" />
<item name="android.permission.BIND_CONNECTION_SERVICE" granted="true" flags="0" />
<item name="android.permission.MANAGE_USB" granted="true" flags="0" />
<item name="android.permission.INTERACT_ACROSS_USERS_FULL" granted="true" flags="0" />
<item name="android.permission.STOP_APP_SWITCHES" granted="true" flags="0" />
<item name="android.permission.BATTERY_STATS" granted="true" flags="0" />
<item name="android.permission.PACKAGE_USAGE_STATS" granted="true" flags="0" />
<item name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" granted="true" flags="0" />
<item name="android.permission.TETHER_PRIVILEGED" granted="true" flags="0" />
<item name="android.permission.WRITE_SECURE_SETTINGS" granted="true" flags="0" />
<item name="android.permission.MOVE_PACKAGE" granted="true" flags="0" />
<item name="android.permission.READ_BLOCKED_NUMBERS" granted="true" flags="0" />
<item name="com.qualcomm.permission.wfd.QC_WFD" granted="true" flags="0" />
<item name="android.permission.READ_SEARCH_INDEXABLES" granted="true" flags="0" />
<item name="android.permission.ACCESS_IMS_CALL_SERVICE" granted="true" flags="0" />
<item name="android.permission.BLUETOOTH_PRIVILEGED" granted="true" flags="0" />
<item name="android.permission.HARDWARE_TEST" granted="true" flags="0" />
<item name="android.intent.category.MASTER_CLEAR.permission.C2D_MESSAGE" granted="true" flags="0" />
<item name="android.permission.BIND_JOB_SERVICE" granted="true" flags="0" />
<item name="android.permission.CONFIRM_FULL_BACKUP" granted="true" flags="0" />
<item name="android.permission.SET_TIME" granted="true" flags="0" />
<item name="android.permission.WRITE_APN_SETTINGS" granted="true" flags="0" />
<item name="android.permission.CHANGE_WIFI_STATE" granted="true" flags="0" />
<item name="android.permission.MANAGE_USERS" granted="true" flags="0" />
<item name="android.permission.SET_PREFERRED_APPLICATIONS" granted="true" flags="0" />
<item name="android.permission.DELETE_CACHE_FILES" granted="true" flags="0" />
<item name="android.permission.ACCESS_NETWORK_STATE" granted="true" flags="0" />
<item name="zq.permissio.CONNECTION_ACTIVATION_SERVICE" granted="true" flags="0" />
<item name="android.permission.DISABLE_KEYGUARD" granted="true" flags="0" />
<item name="android.permission.BACKUP" granted="true" flags="0" />
<item name="android.permission.CHANGE_CONFIGURATION" granted="true" flags="0" />
<item name="android.permission.USER_ACTIVITY" granted="true" flags="0" />
<item name="android.permission.READ_LOGS" granted="true" flags="0" />
<item name="android.permission.COPY_PROTECTED_DATA" granted="true" flags="0" />
<item name="android.permission.INTERACT_ACROSS_USERS" granted="true" flags="0" />
<item name="android.permission.SET_KEYBOARD_LAYOUT" granted="true" flags="0" />
<item name="android.permission.USE_FINGERPRINT" granted="true" flags="0" />
<item name="android.permission.WRITE_USER_DICTIONARY" granted="true" flags="0" />
<item name="android.permission.READ_SYNC_STATS" granted="true" flags="0" />
<item name="android.permission.REBOOT" granted="true" flags="0" />
<item name="android.permission.OEM_UNLOCK_STATE" granted="true" flags="0" />
<item name="android.permission.MANAGE_DEVICE_ADMINS" granted="true" flags="0" />
<item name="android.permission.CHANGE_APP_IDLE_STATE" granted="true" flags="0" />
<item name="android.permission.SET_POINTER_SPEED" granted="true" flags="0" />
<item name="android.permission.MANAGE_NOTIFICATIONS" granted="true" flags="0" />
<item name="android.permission.READ_SYNC_SETTINGS" granted="true" flags="0" />
<item name="android.permission.OVERRIDE_WIFI_CONFIG" granted="true" flags="0" />
<item name="android.permission.FORCE_STOP_PACKAGES" granted="true" flags="0" />
<item name="android.permission.ACCESS_NOTIFICATIONS" granted="true" flags="0" />
<item name="android.permission.VIBRATE" granted="true" flags="0" />
<item name="com.android.certinstaller.INSTALL_AS_USER" granted="true" flags="0" />
<item name="android.permission.READ_USER_DICTIONARY" granted="true" flags="0" />
<item name="android.permission.ACCESS_WIFI_STATE" granted="true" flags="0" />
<item name="android.permission.CHANGE_WIMAX_STATE" granted="true" flags="0" />
<item name="android.permission.MODIFY_PHONE_STATE" granted="true" flags="0" />
<item name="android.permission.STATUS_BAR" granted="true" flags="0" />
<item name="android.permission.READ_FRAME_BUFFER" granted="true" flags="0" />
<item name="android.permission.LOCATION_HARDWARE" granted="true" flags="0" />
<item name="android.permission.WAKE_LOCK" granted="true" flags="0" />
<item name="android.permission.INJECT_EVENTS" granted="true" flags="0" />
<item name="android.permission.DELETE_PACKAGES" granted="true" flags="0" />
</perms>
<proper-signing-keyset identifier="1" />
</package>

所以问题来了,改成系统应用后uid为什么变成1000?后面代表权限组的一串数字又都是什么?

0011 Android File Permission

android_filesystem_config.h

在Android中,所有权限的定义都在:system/core/include/private/android_filesystem_config.h

在这个头文件中定义了Android系统的一些用户,包含root用户,system用户,shell用户所对应的值等等。

/* This is the master Users and Groups config for the platform.
* DO NOT EVER RENUMBER
*/ #define AID_ROOT 0 /* traditional unix root user */ #define AID_SYSTEM 1000 /* system server */ #define AID_RADIO 1001 /* telephony subsystem, RIL */
#define AID_BLUETOOTH 1002 /* bluetooth subsystem */
#define AID_GRAPHICS 1003 /* graphics devices */
#define AID_INPUT 1004 /* input devices */
#define AID_AUDIO 1005 /* audio devices */
#define AID_CAMERA 1006 /* camera devices */
#define AID_LOG 1007 /* log devices */
#define AID_COMPASS 1008 /* compass device */
#define AID_MOUNT 1009 /* mountd socket */
#define AID_WIFI 1010 /* wifi subsystem */
#define AID_ADB 1011 /* android debug bridge (adbd) */
#define AID_INSTALL 1012 /* group for installing packages */
#define AID_MEDIA 1013 /* mediaserver process */
#define AID_DHCP 1014 /* dhcp client */
#define AID_SDCARD_RW 1015 /* external storage write access */
#define AID_VPN 1016 /* vpn system */
#define AID_KEYSTORE 1017 /* keystore subsystem */
#define AID_USB 1018 /* USB devices */
#define AID_DRM 1019 /* DRM server */
#define AID_MDNSR 1020 /* MulticastDNSResponder (service discovery) */
#define AID_GPS 1021 /* GPS daemon */
#define AID_UNUSED1 1022 /* deprecated, DO NOT USE */
#define AID_MEDIA_RW 1023 /* internal media storage write access */
#define AID_MTP 1024 /* MTP USB driver access */
#define AID_UNUSED2 1025 /* deprecated, DO NOT USE */
#define AID_DRMRPC 1026 /* group for drm rpc */
#define AID_NFC 1027 /* nfc subsystem */
#define AID_SDCARD_R 1028 /* external storage read access */
#define AID_CLAT 1029 /* clat part of nat464 */
#define AID_LOOP_RADIO 1030 /* loop radio devices */
#define AID_MEDIA_DRM 1031 /* MediaDrm plugins */
#define AID_PACKAGE_INFO 1032 /* access to installed package details */
#define AID_SDCARD_PICS 1033 /* external storage photos access */
#define AID_SDCARD_AV 1034 /* external storage audio/video access */
#define AID_SDCARD_ALL 1035 /* access all users external storage */
#define AID_LOGD 1036 /* log daemon */
#define AID_SHARED_RELRO 1037 /* creator of shared GNU RELRO files */
#define AID_DBUS 1038 /* dbus-daemon IPC broker process */
#define AID_TLSDATE 1039 /* tlsdate unprivileged user */
#define AID_MEDIA_EX 1040 /* mediaextractor process */
#define AID_AUDIOSERVER 1041 /* audioserver process */
#define AID_METRICS_COLL 1042 /* metrics_collector process */
#define AID_METRICSD 1043 /* metricsd process */
#define AID_WEBSERV 1044 /* webservd process */
#define AID_DEBUGGERD 1045 /* debuggerd unprivileged user */
#define AID_MEDIA_CODEC 1046 /* mediacodec process */
#define AID_CAMERASERVER 1047 /* cameraserver process */
#define AID_FIREWALL 1048 /* firewalld process */
#define AID_TRUNKS 1049 /* trunksd process (TPM daemon) */
#define AID_NVRAM 1050 /* Access-controlled NVRAM */
#define AID_DNS 1051 /* DNS resolution daemon (system: netd) */
#define AID_DNS_TETHER 1052 /* DNS resolution daemon (tether: dnsmasq) */
/* Changes to this file must be made in AOSP, *not* in internal branches. */ #define AID_SHELL 2000 /* adb and debug shell user */
#define AID_CACHE 2001 /* cache access */
#define AID_DIAG 2002 /* access to diagnostic resources */ /* The range 2900-2999 is reserved for OEM, and must never be
* used here */
#define AID_OEM_RESERVED_START 2900
#define AID_OEM_RESERVED_END 2999 /* The 3000 series are intended for use as supplemental group id's only.
* They indicate special Android capabilities that the kernel is aware of. */
#define AID_NET_BT_ADMIN 3001 /* bluetooth: create any socket */
#define AID_NET_BT 3002 /* bluetooth: create sco, rfcomm or l2cap sockets */
#define AID_INET 3003 /* can create AF_INET and AF_INET6 sockets */
#define AID_NET_RAW 3004 /* can create raw INET sockets */
#define AID_NET_ADMIN 3005 /* can configure interfaces and routing tables. */
#define AID_NET_BW_STATS 3006 /* read bandwidth statistics */
#define AID_NET_BW_ACCT 3007 /* change bandwidth statistics accounting */
#define AID_NET_BT_STACK 3008 /* bluetooth: access config files */
#define AID_READPROC 3009 /* Allow /proc read access */
#define AID_WAKELOCK 3010 /* Allow system wakelock read/write access */ /* The range 5000-5999 is also reserved for OEM, and must never be used here. */
#define AID_OEM_RESERVED_2_START 5000
#define AID_OEM_RESERVED_2_END 5999 #define AID_EVERYBODY 9997 /* shared between all apps in the same profile */
#define AID_MISC 9998 /* access to misc storage */
#define AID_NOBODY 9999 #define AID_APP 10000 /* first app user */ #define AID_ISOLATED_START 99000 /* start of uids for fully isolated sandboxed processes */
#define AID_ISOLATED_END 99999 /* end of uids for fully isolated sandboxed processes */ #define AID_USER 100000 /* offset for uid ranges for each user */ #define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
#define AID_SHARED_GID_END 59999 /* start of gids for apps in each user to share */

从头文件定义,就知道每个权限组都是由一串数字代表的。除了数字,该文件还定义了一个结构体数组,映射数字对应的字符串:

static const struct android_id_info android_ids[] = {
{ "root", AID_ROOT, }, { "system", AID_SYSTEM, }, { "radio", AID_RADIO, },
{ "bluetooth", AID_BLUETOOTH, },
{ "graphics", AID_GRAPHICS, },
{ "input", AID_INPUT, },
{ "audio", AID_AUDIO, },
{ "camera", AID_CAMERA, },
{ "log", AID_LOG, },
{ "compass", AID_COMPASS, },
{ "mount", AID_MOUNT, },
{ "wifi", AID_WIFI, },
{ "adb", AID_ADB, },
{ "install", AID_INSTALL, },
{ "media", AID_MEDIA, },
{ "dhcp", AID_DHCP, },
{ "sdcard_rw", AID_SDCARD_RW, },
{ "vpn", AID_VPN, },
{ "keystore", AID_KEYSTORE, },
{ "usb", AID_USB, },
{ "drm", AID_DRM, },
{ "mdnsr", AID_MDNSR, },
{ "gps", AID_GPS, },
// AID_UNUSED1
{ "media_rw", AID_MEDIA_RW, },
{ "mtp", AID_MTP, },
// AID_UNUSED2
{ "drmrpc", AID_DRMRPC, },
{ "nfc", AID_NFC, },
{ "sdcard_r", AID_SDCARD_R, },
{ "clat", AID_CLAT, },
{ "loop_radio", AID_LOOP_RADIO, },
{ "mediadrm", AID_MEDIA_DRM, },
{ "package_info", AID_PACKAGE_INFO, },
{ "sdcard_pics", AID_SDCARD_PICS, },
{ "sdcard_av", AID_SDCARD_AV, },
{ "sdcard_all", AID_SDCARD_ALL, },
{ "logd", AID_LOGD, },
{ "shared_relro", AID_SHARED_RELRO, },
{ "dbus", AID_DBUS, },
{ "tlsdate", AID_TLSDATE, },
{ "mediaex", AID_MEDIA_EX, },
{ "audioserver", AID_AUDIOSERVER, },
{ "metrics_coll", AID_METRICS_COLL },
{ "metricsd", AID_METRICSD },
{ "webserv", AID_WEBSERV },
{ "debuggerd", AID_DEBUGGERD, },
{ "mediacodec", AID_MEDIA_CODEC, },
{ "cameraserver", AID_CAMERASERVER, },
{ "firewall", AID_FIREWALL, },
{ "trunks", AID_TRUNKS, },
{ "nvram", AID_NVRAM, },
{ "dns", AID_DNS, },
{ "dns_tether", AID_DNS_TETHER, }, { "shell", AID_SHELL, },
{ "cache", AID_CACHE, },
{ "diag", AID_DIAG, }, { "net_bt_admin", AID_NET_BT_ADMIN, },
{ "net_bt", AID_NET_BT, },
{ "inet", AID_INET, },
{ "net_raw", AID_NET_RAW, },
{ "net_admin", AID_NET_ADMIN, },
{ "net_bw_stats", AID_NET_BW_STATS, },
{ "net_bw_acct", AID_NET_BW_ACCT, },
{ "net_bt_stack", AID_NET_BT_STACK, },
{ "readproc", AID_READPROC, },
{ "wakelock", AID_WAKELOCK, }, { "everybody", AID_EVERYBODY, },
{ "misc", AID_MISC, },
{ "nobody", AID_NOBODY, },
};

好了,了解这个文件我们再来看我们应用的信息:

com.softard.test 1000 0 /data/user/0/com.softard.test platform 3009,3002,1023,1015,3003,3001,1021,1000,2002,1010,1007

我们把数字对应的信息截取下来:

#define AID_SYSTEM        1000  "system"	/* system server */
#define AID_LOG 1007 "log" /* log devices */
#define AID_WIFI 1010 "wifi" /* wifi subsystem */
#define AID_SDCARD_RW 1015 "sdcard_rw" /* external storage write access */
#define AID_GPS 1021 "gps" /* GPS daemon */
#define AID_MEDIA_RW 1023 "media_rw" /* internal media storage write access */
#define AID_DIAG 2002 "diag" /* access to diagnostic resources */
#define AID_NET_BT_ADMIN 3001 "net_bt_admin"/* bluetooth: create any socket */
#define AID_NET_BT 3002 "net_bt" /* bluetooth: create sco, rfcomm or l2cap sockets */
#define AID_INET 3003 "inet" /* can create AF_INET and AF_INET6 sockets */
#define AID_READPROC 3009 "readproc" /* Allow /proc read access */

所以,当我指定应用为系统应用时,就将uid指定为了1000。并且拥有了各种属于系统的权限组。

那么,指定系统应用后是怎么获得这一系列的gid呢?这一块源码流程很多,放到后面集中分析。

好了,现在我们了解system/core/include/private/android_filesystem_config.h文件定义以后,其实就前进了一大步。注意一下代码所在源码位置,位于system/core下,其实它已经是Android内核部分了,所以后面涉及到权限内容,都会导入这个头文件。

现在来看一段源码再来熟悉一下这部分内容:

FileUtils.setPermissions(fstr.getFD(), 0640, SYSTEM_UID, PACKAGE_INFO_GID);

这是位于frameworks/base/services/core/java/com/android/server/pm/Settings.java的一段代码,功能就是创建packages.list文件。从函数名称和参数可以知道,它是给文件添加权限的,通过这段代码可以推测出:它给packages.list文件赋予了0640的权限,权限隶属于system,权限组为package_info。

然后我们进入系统看一看是不是这样:

/data/system # ls -l packages.list
-rw-r----- 1 system package_info 13627 1970-01-01 11:27 packages.list

有没有发现原来Android底层权限其实也不是很难理解嘛?

这时,又有一个问题了,创建文件你可以这样设定权限,但系统文件/文件夹的默认权限又是从哪来的?这就要引入另外一个文件了。

fs_config.c

写过Android App的你肯定知道,App的一些数据都放在/data/data目录下。正常情况下这个目录是不可以访问的:

/data/data $ ls
ls: .: Permission denied

我们看一下这个目录的权限:

/data/data $ ls -ld
drwxrwx--x 310 system system 12288 2019-01-11 09:51 .

我们作为shell用户,只有一个x权限, 当然访问不了。那如果我要给shell赋予访问权限改怎么改呢?

这就需要了解一下system/core/libcutils/fs_config.c文件了。系统目录和文件的用户组以及权限都是在这个文件里定义的:

/* Rules for directories.
** These rules are applied based on "first match", so they
** should start with the most specific path and work their
** way up to the root.
*/
static const struct fs_path_config android_dirs[] = {
{ 00770, AID_SYSTEM, AID_CACHE, 0, "cache" },
{ 00500, AID_ROOT, AID_ROOT, 0, "config" },
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app" },
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private" },
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app-ephemeral" },
{ 00771, AID_ROOT, AID_ROOT, 0, "data/dalvik-cache" },
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/data" },
{ 00771, AID_SHELL, AID_SHELL, 0, "data/local/tmp" },
{ 00771, AID_SHELL, AID_SHELL, 0, "data/local" },
{ 01771, AID_SYSTEM, AID_MISC, 0, "data/misc" },
{ 00770, AID_DHCP, AID_DHCP, 0, "data/misc/dhcp" },
{ 00771, AID_SHARED_RELRO, AID_SHARED_RELRO, 0, "data/misc/shared_relro" },
{ 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media" },
{ 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/Music" },
{ 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest" },
{ 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest64" },
{ 00775, AID_ROOT, AID_ROOT, 0, "data/preloads" },
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data" },
{ 00755, AID_ROOT, AID_SYSTEM, 0, "mnt" },
{ 00755, AID_ROOT, AID_ROOT, 0, "root" },
{ 00750, AID_ROOT, AID_SHELL, 0, "sbin" },
{ 00751, AID_ROOT, AID_SDCARD_R, 0, "storage" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/vendor" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/xbin" },
{ 00755, AID_ROOT, AID_ROOT, 0, "system/etc/ppp" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor" },
{ 00777, AID_ROOT, AID_ROOT, 0, "sdcard" },
{ 00755, AID_ROOT, AID_ROOT, 0, 0 },
}; /* Rules for files.
** These rules are applied based on "first match", so they
** should start with the most specific path and work their
** way up to the root. Prefixes ending in * denotes wildcard
** and will allow partial matches.
*/
static const char conf_dir[] = "/system/etc/fs_config_dirs";
static const char conf_file[] = "/system/etc/fs_config_files"; static const struct fs_path_config android_files[] = {
{ 00440, AID_ROOT, AID_SHELL, 0, "system/etc/init.goldfish.rc" },
{ 00550, AID_ROOT, AID_SHELL, 0, "system/etc/init.goldfish.sh" },
{ 00550, AID_ROOT, AID_SHELL, 0, "system/etc/init.ril" },
{ 00555, AID_ROOT, AID_ROOT, 0, "system/etc/ppp/*" },
{ 00555, AID_ROOT, AID_ROOT, 0, "system/etc/rc.*" },
{ 00440, AID_ROOT, AID_ROOT, 0, "system/etc/recovery.img" },
{ 00444, AID_ROOT, AID_ROOT, 0, conf_dir + 1 },
{ 00444, AID_ROOT, AID_ROOT, 0, conf_file + 1 },
{ 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app/*" },
{ 00644, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/*" },
{ 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private/*" },
{ 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app-ephemeral/*" },
{ 00644, AID_APP, AID_APP, 0, "data/data/*" },
{ 00640, AID_ROOT, AID_SHELL, 0, "data/nativetest/tests.txt" },
{ 00640, AID_ROOT, AID_SHELL, 0, "data/nativetest64/tests.txt" },
{ 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest/*" },
{ 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest64/*" }, /* the following two files are INTENTIONALLY set-uid, but they
* are NOT included on user builds. */
{ 04750, AID_ROOT, AID_SHELL, 0, "system/xbin/su" },
{ 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/procmem" }, /* the following files have enhanced capabilities and ARE included in user builds. */
{ 00750, AID_ROOT, AID_SHELL, CAP_MASK_LONG(CAP_SETUID) | CAP_MASK_LONG(CAP_SETGID), "system/bin/run-as" },
{ 00700, AID_SYSTEM, AID_SHELL, CAP_MASK_LONG(CAP_BLOCK_SUSPEND), "system/bin/inputflinger" }, /* Support FIFO scheduling mode in SurfaceFlinger. */
{ 00755, AID_SYSTEM, AID_GRAPHICS, CAP_MASK_LONG(CAP_SYS_NICE), "system/bin/surfaceflinger" }, { 00750, AID_ROOT, AID_ROOT, 0, "system/bin/uncrypt" },
{ 00750, AID_ROOT, AID_ROOT, 0, "system/bin/install-recovery.sh" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" },
{ 00755, AID_ROOT, AID_ROOT, 0, "system/lib/valgrind/*" },
{ 00755, AID_ROOT, AID_ROOT, 0, "system/lib64/valgrind/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/xbin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/vendor/bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/vendor/xbin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor/bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor/xbin/*" },
{ 00750, AID_ROOT, AID_SHELL, 0, "sbin/*" },
{ 00755, AID_ROOT, AID_ROOT, 0, "bin/*" },
{ 00750, AID_ROOT, AID_SHELL, 0, "init*" },
{ 00750, AID_ROOT, AID_SHELL, 0, "sbin/fs_mgr" },
{ 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" },
{ 00644, AID_ROOT, AID_ROOT, 0, 0 },
};

感觉也不用多说,android_dirs[]负责文件夹的权限配置,android_files[]负责文件的权限配置。

从里面定义找找刚提到的/data/data目录

{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/data" },

你看,就是这么回事儿而已。如果要改成shell权限的话,只需要这样改就可以了:

{ 00771, AID_SYSTEM, AID_SHELL, 0, "data/data" },

当然,如果系统里还要添加其他目录、文件需要指定权限,只需要在这个文件里添加一行即可。

0100 Android App Permission

讲到这里,还遗留一个开头提出的问题:

为什么你在AndroidManifest文件请求storage权限你就可以访问设备文件?

0010里提到过,PackageManagerService在启动后会扫描所有已经安装的App,然后加载和解析他们的Androidmanifest文件,生成packages.listpackages.xml等文件。解析过程就包括了permission的解析与拦截。

frameworks/base/core/res/AndroidManifest.xml 这个文件中定义了Permission拦截规则,下面列举几个:

<!-- Allows an application to read from external storage.
<p>Any app that declares the {@link #WRITE_EXTERNAL_STORAGE} permission is implicitly
granted this permission.</p>
<p>This permission is enforced starting in API level 19. Before API level 19, this
permission is not enforced and all apps still have access to read from external storage.
You can test your app with the permission enforced by enabling <em>Protect USB
storage</em> under Developer options in the Settings app on a device running Android 4.1 or
higher.</p>
<p>Also starting in API level 19, this permission is <em>not</em> required to
read/write files in your application-specific directories returned by
{@link android.content.Context#getExternalFilesDir} and
{@link android.content.Context#getExternalCacheDir}.
<p class="note"><strong>Note:</strong> If <em>both</em> your <a
href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
minSdkVersion}</a> and <a
href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
targetSdkVersion}</a> values are set to 3 or lower, the system implicitly
grants your app this permission. If you don't need this permission, be sure your <a
href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
targetSdkVersion}</a> is 4 or higher.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:permissionGroup="android.permission-group.STORAGE"
android:label="@string/permlab_sdcardRead"
android:description="@string/permdesc_sdcardRead"
android:protectionLevel="dangerous" /> <!-- Allows applications to access information about Wi-Fi networks.
<p>Protection level: normal
-->
<permission android:name="android.permission.ACCESS_WIFI_STATE"
android:description="@string/permdesc_accessWifiState"
android:label="@string/permlab_accessWifiState"
android:protectionLevel="normal" /> <!-- @SystemApi Allows applications to set the system time.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_TIME"
android:protectionLevel="signature|privileged" />

这里看一下protectionLevel,normal是一般权限,即不需要动态申请,直接在Manifest里注册即可获得的权限。dangerous是敏感权限,需要动态申请告知用户才能获取的权限。signature|privileged一般是系统priv-app才拥有的权限,也就是拥有系统签名的系统应用。

他的拦截规则大概是,如果App申请了signature|privileged权限,但他是普通开发者的三方App,PMS就会将其从申请权限的列表里将该权限删除。这样你的App实际上就没有获得对应的权限了。

那么,文件属性的权限是怎么和Permission联系起来的?不出意外,系统也有一个关联文件的定义frameworks/base/data/etc/platform.xml

<!-- This file is used to define the mappings between lower-level system
user and group IDs and the higher-level permission names managed
by the platform. Be VERY careful when editing this file! Mistakes made here can open
big security holes.
-->
<permissions> <!-- ================================================================== -->
<!-- ================================================================== -->
<!-- ================================================================== --> <!-- The following tags are associating low-level group IDs with
permission names. By specifying such a mapping, you are saying
that any application process granted the given permission will
also be running with the given group ID attached to its process,
so it can perform any filesystem (read, write, execute) operations
allowed for that group. --> <permission name="android.permission.BLUETOOTH_ADMIN" >
<group gid="net_bt_admin" />
</permission> <permission name="android.permission.BLUETOOTH" >
<group gid="net_bt" />
</permission> <permission name="android.permission.BLUETOOTH_STACK" >
<group gid="net_bt_stack" />
<group gid="wakelock" />
</permission> <permission name="android.permission.NET_TUNNELING" >
<group gid="vpn" />
</permission> <permission name="android.permission.INTERNET" >
<group gid="inet" />
</permission> <permission name="android.permission.READ_LOGS" >
<group gid="log" />
</permission> <permission name="android.permission.WRITE_MEDIA_STORAGE" >
<group gid="media_rw" />
<group gid="sdcard_rw" />
</permission> <permission name="android.permission.ACCESS_MTP" >
<group gid="mtp" />
</permission> <permission name="android.permission.NET_ADMIN" >
<group gid="net_admin" />
</permission> <!-- The group that /cache belongs to, linked to the permission
set on the applications that can access /cache -->
<permission name="android.permission.ACCESS_CACHE_FILESYSTEM" >
<group gid="cache" />
</permission> <!-- RW permissions to any system resources owned by group 'diag'.
This is for carrier and manufacture diagnostics tools that must be
installable from the framework. Be careful. -->
<permission name="android.permission.DIAGNOSTIC" >
<group gid="input" />
<group gid="diag" />
</permission> <!-- Group that can read detailed network usage statistics -->
<permission name="android.permission.READ_NETWORK_USAGE_HISTORY">
<group gid="net_bw_stats" />
</permission> <!-- Group that can modify how network statistics are accounted -->
<permission name="android.permission.MODIFY_NETWORK_ACCOUNTING">
<group gid="net_bw_acct" />
</permission> <permission name="android.permission.LOOP_RADIO" >
<group gid="loop_radio" />
</permission> <!-- Hotword training apps sometimes need a GID to talk with low-level
hardware; give them audio for now until full HAL support is added. -->
<permission name="android.permission.MANAGE_VOICE_KEYPHRASES">
<group gid="audio" />
</permission> <permission name="android.permission.ACCESS_FM_RADIO" >
<!-- /dev/fm is gid media, not audio -->
<group gid="media" />
</permission> <!-- These are permissions that were mapped to gids but we need
to keep them here until an upgrade from L to the current
version is to be supported. These permissions are built-in
and in L were not stored in packages.xml as a result if they
are not defined here while parsing packages.xml we would
ignore these permissions being granted to apps and not
propagate the granted state. From N we are storing the
built-in permissions in packages.xml as the saved storage
is negligible (one tag with the permission) compared to
the fragility as one can remove a built-in permission which
no longer needs to be mapped to gids and break grant propagation. -->
<permission name="android.permission.READ_EXTERNAL_STORAGE" />
<permission name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- ================================================================== -->
<!-- ================================================================== -->
<!-- ================================================================== --> <!-- The following tags are assigning high-level permissions to specific
user IDs. These are used to allow specific core system users to
perform the given operations with the higher-level framework. For
example, we give a wide variety of permissions to the shell user
since that is the user the adb shell runs under and developers and
others should have a fairly open environment in which to
interact with the system. --> <assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="media" />
<assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="media" />
<assign-permission name="android.permission.WAKE_LOCK" uid="media" />
<assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="media" />
<assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="media" />
<assign-permission name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" uid="media" /> <assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="audioserver" />
<assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="audioserver" />
<assign-permission name="android.permission.WAKE_LOCK" uid="audioserver" />
<assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="audioserver" />
<assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="audioserver" /> <assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="cameraserver" />
<assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="cameraserver" />
<assign-permission name="android.permission.WAKE_LOCK" uid="cameraserver" />
<assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="cameraserver" />
<assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="cameraserver" /> <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" /> <!-- This is a list of all the libraries available for application
code to link against. --> <library name="android.test.runner"
file="/system/framework/android.test.runner.jar" />
<library name="javax.obex"
file="/system/framework/javax.obex.jar" />
<library name="org.apache.http.legacy"
file="/system/framework/org.apache.http.legacy.jar" /> <!-- These are the standard packages that are white-listed to always have internet
access while in power save mode, even if they aren't in the foreground. -->
<allow-in-power-save package="com.android.providers.downloads" /> <!-- These are the standard packages that are white-listed to always have internet
access while in data mode, even if they aren't in the foreground. -->
<allow-in-data-usage-save package="com.android.providers.downloads" /> <!-- These are the packages that are white-listed to be able to run as system user -->
<system-user-whitelisted-app package="com.android.settings" /> <!-- These are the packages that shouldn't run as system user -->
<system-user-blacklisted-app package="com.android.wallpaper.livepicker" />
</permissions>

这个文件定义了所有权限所属的gid。从里面找一下READ_EXTERNAL_STORAGE权限,emmmm,什么都没做。这是因为6.0之后存储权限变成动态,需要用户确认才可以获取权限,所以这里不作处理。动态权限这部分代码先不分析了,来看一下老版本的文件:

<permission name="android.permission.READ_EXTERNAL_STORAGE" >
<group gid="sdcard_r" />
</permission>

那明确了,READ_EXTERNAL_STORAGE权限获取的gid是sdcard_r,然后查看上面的文件定义,对应AID_SDCARD_R,数值是1028。实际上在7.0上得到的是sdcard_rw,即1015。

PMS在解析每个Permission时会根据这个文件将Permission关联的gid 加入到一个gid的数组中去,从而硬件设备所对应的设备文件就能被该应用程序访问。这块具体代码流程放到下一篇去分析。想自己跟代码的话可以从该函数看起:

private void grantRequestedRuntimePermissions(PackageParser.Package pkg, int[] userIds,
String[] grantedPermissions) {
for (int userId : userIds) {
grantRequestedRuntimePermissionsForUser(pkg, userId, grantedPermissions);
} // We could have touched GID membership, so flush out packages.list
synchronized (mPackages) {
mSettings.writePackageListLPr();
}
}

回到存储权限,既然系统会根据permission给App添加合适的gid,那么我们在看下内置存储的权限为

/sdcard # ls -ld
drwxrwx--x 27 root sdcard_rw 4096 1970-01-01 18:30 .

所以获得sdcard_rw权限组的应用才可以访问内置存储。

到此Android权限的底层实现原理简单介绍完了,不过目前这里还留有一个坑,那就是我在测试App里添加STORAGE权限后,安装到设备里,通过查看进程属性,发现:

# ps | grep softard
u0_a53 3530 638 969076 30720 binder_thr b07244fc S com.softard.test
# cat /proc/3530/status
Name: com.softard.test
State: S (sleeping)
Tgid: 3530
Pid: 3530
PPid: 638
TracerPid: 0
Uid: 10053 10053 10053 10053
Gid: 10053 10053 10053 10053
Ngid: 0
FDSize: 256
Groups: 9997 50053

Groups没有对应的gid,但是程序的确可以访问/sdcard

然后我又给App系统签名,作为系统应用放进去再看,

# ps | grep soft
system 3560 629 985352 49184 SyS_epoll_ abea53b8 S com.softard.test
# cat /proc/3560/status
Name: com.softard.test
State: S (sleeping)
Tgid: 3560
Pid: 3560
PPid: 629
TracerPid: 0
Uid: 1000 1000 1000 1000
Gid: 1000 1000 1000 1000
Ngid: 0
FDSize: 256
Groups: 1000 1007 1010 1015 1021 1023 2002 2950 3001 3002 3003 3009 9997 41000

这时候Groups有sdcard_rw权限了。然后从设置里手动关掉存储权限,App无法读取文件,再次检查gid发现这个1015依旧存在。

WTF,7.0的表现跟5.0完全不一样?又是一个坑…后面抽空再填吧...

行吧,这篇概览就这样了,后面就开始从眼花缭乱的源码角度去看这一切的实现。

0101 Reference

Android文件属性的权限和Permission的联系

全方位理解Android权限之底层实现概览的更多相关文章

  1. 抽丝剥茧:理解Android权限机制

    前一段时间面试官问我Android在Linux的基础上,权限做了哪些改变.霹雳呱啦说了一堆,但是说着说着,始终感觉自己说的缺了点东西,自己理解还是不够到位,而且网上的很多文章在原理上基本都是大同小异, ...

  2. Android开发之深入理解Android 7.0系统权限更改相关文档

    http://www.cnblogs.com/dazhao/p/6547811.html 摘要: Android 6.0之后的版本增加了运行时权限,应用程序在执行每个需要系统权限的功能时,需要添加权限 ...

  3. 深入理解Android之Gradle

    深入理解Android之Gradle 格式更加精美的PDF版请到:http://vdisk.weibo.com/s/z68f8l0xTYrZt 下载 Gradle是当前非常"劲爆" ...

  4. Android权限说明 system权限 root权限

    原文链接:http://blog.csdn.net/rockwupj/article/details/8618655 Android权限说明 Android系统是运行在Linux内核上的,Androi ...

  5. 《深入理解Android 卷III》第八章深入理解Android壁纸

    <深入理解Android 卷III>即将公布,作者是张大伟. 此书填补了深入理解Android Framework卷中的一个主要空白,即Android Framework中和UI相关的部分 ...

  6. 《深入理解Android 卷III》第六章 深入理解控件(ViewRoot)系统

    <深入理解Android 卷III>即将公布,作者是张大伟.此书填补了深入理解Android Framework卷中的一个主要空白,即Android Framework中和UI相关的部分. ...

  7. android权限大全

    访问登记属性 android.permission.ACCESS_CHECKIN_PROPERTIES ,读取或写入登记check-in数据库属性表的权限 获取错略位置 android.permiss ...

  8. [转载] 深入理解Android之Java虚拟机Dalvik

    本文转载自: http://blog.csdn.net/innost/article/details/50377905 一.背景 这个选题很大,但并不是一开始就有这么高大上的追求.最初之时,只是源于对 ...

  9. android权限permission大全

    1.Android.permission.WRITE_USER_DICTIONARY允许应用程序向用户词典中写入新词 2.android.permission.WRITE_SYNC_SETTINGS写 ...

随机推荐

  1. PHP计算连续签到天数以及累计签到天数

    代码如下: /** * 统计连续签到天数以及累计签到天数 * @param string $user_long_id 用户ID * @return array 一维数组 */ function sig ...

  2. Vue.js + Nuxt.js 项目中使用 Vee-validate 表单校验

    vee-validate 是为 Vue.js 量身打造的表单校验框架,允许您校验输入的内容并显示对应的错误提示信息.它内置了很多常见的校验规则,可以组合使用多种校验规则,大部分场景只需要配置就能实现开 ...

  3. Linux安全配置

    注释掉系统不需要的用户和用户组 vi /etc/passwd #adm:x:3:4:adm:/var/adm:/sbin/nologin #lp:x:4:7:lp:/var/spool/lpd:/sb ...

  4. Go pprof性能监控

    Go net/http/pprof包提供了一个在WEB项目中使用的性能监控的工具, 使用时只需要引用包: _"net/http/pprof" 然后就可以在浏览器中访问地址: htt ...

  5. Go基础系列:函数(1)

    Go中函数特性简介 对Go中的函数特性做一个总结.懂则看,不懂则算. Go中有3种函数:普通函数.匿名函数(没有名称的函数).方法(定义在struct上的函数). Go编译时不在乎函数的定义位置,但建 ...

  6. μC/OS-II 中的任务管理

    1. 任务的状态及其转换 睡眠状态: 任务在没有被配备任务控制块或被剥夺了任务控制块时的状态叫做任务的睡眠状态. 等待状态: 正在运行的任务,需要等待一段时间或需要等待一个事件发生再运行时,该任务就会 ...

  7. 分布式系统监视zabbix讲解四之可视化--技术流ken

    图形 概述 随着大量的监控数据被采集到Zabbix中,如果用户可以以可视化的表现形式来查看发生了什么事情,那么和仅仅只有数字的表现形式比起来则更加轻松. 以下是进行图形设置的地方.图形可以一目了然地掌 ...

  8. Java核心技术 对象与类

    对象与对象变量: 要想使用对象,就必须首先构造对象,并指定其初始状态,然后,对对象应用方法. 在Java中,使用构造器构造新实例.构造器是一种特殊的方法,用来构造并初始化对象. 在实际开发中,通常需要 ...

  9. IdentityServer开题篇

    最近也研究了一段时间的IdentityServer4,园里关于IdentityServer的文章也很多,这里也简单写写,做做记录(文笔不佳,见谅). 1.identityserver是什么? Iden ...

  10. SqlServer中循环和条件语句

    if语句使用示例 declare @a int              set @a=12              if @a>100                 begin      ...