CheckPermission: примеры (JAVA)

Проверка прав доступа через checkPermission
Раздел: Безопасность (Security), Контроль доступа
checkPermission(Permission perm): void

Общее описание метода checkPermission

В экосистеме Java под именем checkPermission встречается несколько методов, предназначенных для проверки прав доступа. Основные варианты расположены в классах SecurityManager, java.security.AccessController, а также в Android API - Context.checkPermission и PackageManager.checkPermission. Назначение одинаково в семантике: установить, предоставлено ли конкретное право (permission) для потока, процесса или пакета, и в зависимости от реализации вернуть код результата или бросить исключение.

Краткое сравнение по сигнатурам и поведению:

  • void SecurityManager.checkPermission(Permission perm) - классический метод, проверяющий разрешение для текущего контекста безопасности. При отсутствии права бросает SecurityException. Возвращаемого значения нет.
  • void AccessController.checkPermission(Permission perm) - проверка в рамках механизма контроля доступа. При отсутствии права бросает AccessControlException.
  • int Context.checkPermission(String permission, int pid, int uid) (Android) - возвращает PackageManager.PERMISSION_GRANTED или PackageManager.PERMISSION_DENIED в зависимости от наличия разрешения у указанного процесса/пользователя.
  • int PackageManager.checkPermission(String permName, String pkgName) (Android) - проверяет, присвоено ли приложению с пакетом pkgName указанное разрешение. Возвращает те же константы, что и выше.

Параметры и возвращаемые значения

  • Permission perm (java.security.Permission) - объект, описывающий ресурс и права (например, new FilePermission("/tmp/*", "read")). Методы в ядре Java используют этот объект и бросают исключение при нарушении.
  • String permission (Android) - имя разрешения из манифеста или константа из android.Manifest.permission.
  • int pid, int uid (Android Context.checkPermission) - идентификатор процесса и пользователя, для которого проверяется право. Часто используются Process.myPid() и Process.myUid() для проверки для текущего процесса.
  • String pkgName (PackageManager.checkPermission) - имя пакета приложения, для которого выполняется проверка.
  • Возвращаемые значения для Android: PackageManager.PERMISSION_GRANTED (обычно 0) при наличии права и PackageManager.PERMISSION_DENIED при его отсутствии.
  • Возвращаемые значения для Java core: отсутствие значения, вместо этого используется возбуждение исключения (SecurityException или AccessControlException).

Поведение может зависеть от установленной Policy (java.security.Policy) и текущего менеджера безопасности. В Android дополнительно учитывается модель runtime-пермишнов (начиная с Android 6.0) и контекст выполнения приложения.

Краткие примеры использования

1. SecurityManager.checkPermission - пример с выбросом исключения:

import java.security.*;
import java.io.*;

public class SecCheck {
    public static void main(String[] args) {
        SecurityManager sm = System.getSecurityManager();
        Permission perm = new FilePermission("/tmp/secret.txt", "read");
        try {
            if (sm != null) sm.checkPermission(perm);
            System.out.println("allowed");
        } catch (SecurityException e) {
            System.out.println("denied: " + e.getMessage());
        }
    }
}
denied: access denied (java.io.FilePermission /tmp/secret.txt read)

2. AccessController.checkPermission - проверка в привилегированном блоке:

import java.security.*;

public class ACCheck {
    public static void main(String[] args) {
        Permission perm = new RuntimePermission("exitVM");
        try {
            AccessController.checkPermission(perm);
            System.out.println("exitVM permitted");
        } catch (AccessControlException e) {
            System.out.println("not permitted: " + e.getPermission());
        }
    }
}
not permitted: (java.lang.RuntimePermission exitVM)

3. Android: Context.checkPermission для текущего процесса:

// внутри Activity или Context
int res = checkPermission(android.Manifest.permission.CAMERA,
                          android.os.Process.myPid(),
                          android.os.Process.myUid());
if (res == android.content.pm.PackageManager.PERMISSION_GRANTED) {
    System.out.println("granted");
} else {
    System.out.println("denied");
}
denied

4. Android: PackageManager.checkPermission для другого пакета:

PackageManager pm = getPackageManager();
int r = pm.checkPermission(android.Manifest.permission.INTERNET, "com.example.app");
System.out.println(r == PackageManager.PERMISSION_GRANTED ? "granted" : "denied");
granted

Похожие методы в Java и их особенности

  • AccessController.checkPermission - альтернатива SecurityManager; используется в контексте менеджера доступа на уровне класслоадеров и доменов защиты. Возбуждает AccessControlException.
  • Policy.getPolicy().implies(ProtectionDomain, Permission) - позволяет напрямую спросить, подразумевает ли текущая политика конкретное разрешение для домена защиты.
  • Permission.implies - метод у конкретных Permission-классов для локальной логики сопоставления (например, FilePermission). Полезно для пользовательских реализаций прав.

Рекомендации по использованию: если требуется стандартная проверка в рантайме для класса/потока, чаще применяется AccessController в новых модулях; для совместимости с существующим кодом может использоваться SecurityManager, однако он помечен как устаревающий. Для политик на уровне приложения и тестирования выгодно использовать Policy и Permission.implies.

Эквиваленты в других языках и их отличия

  • Python: os.access(path, mode) - проверка прав доступа к файлу на уровне ОС; возвращает булево. Пример:
    import os
    print(os.access('/tmp/secret.txt', os.R_OK))
    False
    Отличие: проверка файловых прав, не модель прав приложения/пользователя Java.
  • JavaScript (браузер): navigator.permissions.query({name: 'geolocation'}) - асинхронная проверка разрешения. Пример:
    navigator.permissions.query({name:'geolocation'}).then(p => console.log(p.state));
    "prompt"
    Отличие: модель браузерных разрешений, асинхронная и декларативная.
  • Node.js: fs.access(path, fs.constants.R_OK, cb) - проверка доступности файла. Возвращает ошибку в колбэке или null. Отличие - файловая проверка, не ролевая модель.
  • PHP: is_readable(), is_writable() - простые проверки прав на файлы. Для ролей приложений используются фреймворки и проверки прав в БД.
  • C#/.NET: PrincipalPermission.Demand() или WindowsPrincipal.IsInRole() - проверка прав пользователя/роли. Пример:
    using System.Security.Principal;
    Console.WriteLine(new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole("Administrators"));
    False
    Отличие: встроенная модель ролей и декларативные разрешения, а не Permission-объекты Java.
  • Go: os.Stat + mode.Perm() или syscall.Access для проверки файловых прав. Пример:
    import ("os");
    fi, _ := os.Stat("/tmp/secret.txt");
    fmt.Println(fi.Mode().Perm() & 0400 != 0)
    false
    Отличие: OS-ориентированная проверка, отсутствие единой модели пермишенов для приложения.
  • Kotlin: использует те же API, что и Java, поэтому checkPermission применяется идентично.
  • Lua: в чистом языке нет встроенной модели прав; проверка часто делегируется ОС или окружению (например, читать файл через io.open и проверять результат).

Типичные ошибки при использовании checkPermission

  • Ожидание булевого результата от SecurityManager.checkPermission. В действительности метод возвращает void и при отсутствии права бросает исключение. Пример ошибки:
    SecurityManager sm = System.getSecurityManager();
    if (sm.checkPermission(perm)) { ... } // компиляция не пройдёт
    Ошибка компиляции: incompatible types: unexpected return value
  • Неправильное использование Android API: вызов checkPermission без указания pid/uid для чужого процесса или ожидание, что метод заменяет механизм runtime-permissions. Пример:
    // неверно - проверяет не для текущего процесса
    int r = checkPermission(Manifest.permission.CAMERA, 0, 0);
    denied
  • Игнорирование возможности null у System.getSecurityManager(). При отсутствии менеджера безопасности вызов checkPermission у sm приведёт к NPE. Рекомендуется сначала проверить на null.
  • Смешение уровней: ожидание, что проверка файловых прав ОС эквивалентна проверке Java Permission. FilePermission и права ОС могут не совпадать в контейнеризованных средах.

Изменения и эволюция поведения

  • SecurityManager помечен как устаревший (deprecated for removal) начиная с Java 17 (JEP 411). Это влияет на применение SecurityManager.checkPermission в новых версиях JDK: рекомендуется планировать миграцию от модели SecurityManager к другим механизмам контроля доступа.
  • Механизм AccessController и модель Policy остаются в платформе, но для новых приложений чаще применяются специализированные средства безопасности (контейнеры, ограниченные рантаймы).
  • В Android возникла модель runtime-permissions начиная с Android 6.0 (Marshmallow). До этого достаточно было декларации в манифесте; теперь для определённых опасных разрешений требуется динамический запрос у пользователя и проверка через checkSelfPermission / requestPermissions.

Расширенные и редкие варианты использования

1. Создание собственного Permission и проверка через AccessController

Пример java
import java.security.*;

public final class MyPermission extends Permission {
    public MyPermission(String name) { super(name); }
    public boolean implies(Permission p) { return equals(p); }
    public boolean equals(Object o) { return (o instanceof MyPermission) && getName().equals(((MyPermission)o).getName()); }
    public int hashCode() { return getName().hashCode(); }
    public PermissionCollection newPermissionCollection() { return null; }
}

// использование
try {
    AccessController.checkPermission(new MyPermission("useSpecialFeature"));
    System.out.println("ok");
} catch (AccessControlException e) {
    System.out.println("forbidden");
}
forbidden

Пояснение: собственные Permission позволяют описать специфичные для приложения права и использовать инфраструктуру AccessController/Policy.

2. DoPrivileged в сочетании с проверкой прав

Пример java
Permission perm = new RuntimePermission("getClassLoader");
try {
    AccessController.doPrivileged((PrivilegedAction) () -> {
        // локальная привилегированная операция
        return null;
    }, new AccessControlContext(new ProtectionDomain[]{new ProtectionDomain(null, new Permissions())}));
    AccessController.checkPermission(perm);
    System.out.println("allowed within privileged context");
} catch (AccessControlException e) {
    System.out.println("denied");
}
denied

Пояснение: применение doPrivileged влияет на стек проверки и позволяет выполнять действия под повышенными привилегиями при корректной конфигурации политик.

3. Android: проверка нескольких разрешений и запрос отсутствующих у пользователя

Пример java
String[] perms = {Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO};
List<String> miss = new ArrayList<>();
for (String p: perms) {
    if (checkSelfPermission(p) != PackageManager.PERMISSION_GRANTED) miss.add(p);
}
if (!miss.isEmpty()) {
    requestPermissions(miss.toArray(new String[0]), 123);
} else {
    System.out.println("all granted");
}
all granted

Пояснение: на Android часто требуется одновременно проверять набор разрешений и запрашивать у пользователя отсутствующие.

4. Проверка разрешений для другого приложения (Android) - сценарий для системных компонентов или менеджеров

Пример java
PackageManager pm = getPackageManager();
int r = pm.checkPermission(Manifest.permission.SEND_SMS, "com.example.client");
if (r == PackageManager.PERMISSION_GRANTED) {
    // разрешено отправлять SMS
}
(зависит от настроек на устройстве)

Пояснение: проверка прав другого пакета требует прав доступа у проверяющего процесса и актуальна для сервисов управления правами, антивирусов и MDM-решений.

джава checkPermission function comments

En
CheckPermission Checks if the specified permission is granted