Java Authentication and Authorization Service
Java Authentication and Authorization Service (англ. Java Authentication and Authorization Service, JAAS) — это компонент платформы Java, представляющий собой реализацию стандартной концепции плагин-модулей аутентификации (PAM) для языка Java[1].
Основное описание
JAAS является стандартной частью платформы Java с версии Java 2 SDK, Standard Edition (J2SDK), начиная с версии 1.4. В версии 1.3 JAAS входил в состав опциональных библиотек. Внедрение JAAS было связано с низкой гибкостью первоначальной системы безопасности, особенно при разработке серверных приложений. Как часть системы безопасности Java 2, JAAS обеспечивает выдачу разрешений и контроль их соблюдения. Все проверки выполняются вне кода приложения, поэтому нет необходимости модифицировать исходный код. Приложение при этом не осведомлено о факте аутентификации и авторизации.
JAAS используется для решения двух задач:
- Аутентификация — проверка подлинности пользователя (например, путём ввода имени пользователя и пароля). Такой способ обеспечивает безопасное и надёжное подтверждение личности.
- Авторизация — определение, имеет ли аутентифицированный пользователь права доступа к определённой части системы.
В связи с удалением Security Manager (начиная с Java 17, JEP 411) встроенный механизм авторизации JAAS на основе политик больше не функционирует. Роль JAAS сместилась исключительно на аутентификацию, а авторизация теперь должна реализовываться на уровне самого приложения[2][3].
Состав фреймворка
- Subject — представляет источник запроса (пользователя или сервис). После аутентификации с Subject могут быть связаны один или несколько объектов Principal и Credential[1].
- Principal — представляет сущность пользователя. Может быть, например, табельным номером, именем и т. д. Также отражает роли пользователя в системе; у Subject может быть несколько ролей.
- Credential — объект, доказывающий личность пользователя (имя и пароль, отпечаток пальца и т. д.). Это не часть библиотеки JAAS, так как любая реализация может выступать в роли Credential. Рекомендуется реализовать методы Refreshable и Destroyable, позволяющие обновлять или уничтожать объект. Различают публичные (например, имя) и приватные (например, пароль) Credential.
- LoginContext — содержит базовые методы для аутентификации Subject.
- LoginModule — интерфейс для интеграции пользовательских технологий аутентификации.
- CallbackHandler — интерфейс для связки между пользователем и LoginModule.
- Callback — интерфейс для передачи различных аутентификационных сведений.
- Policy — абстрактный класс, описывающий политику безопасности: определяет разрешения, выданные коду, либо пользователю (Principal). Класс помечен как устаревший и подлежащий удалению из-за отказа от Security Manager[4].
- AuthPermission — базовый класс разрешений в JAAS, использующий предопределённые имена для отдельных видов разрешений. Помечен как устаревший и подлежащий удалению из-за отказа от Security Manager[5].
- PrivateCredentialPermission — класс для защиты приватных Credential. Помечен как устаревший и подлежащий удалению в будущих версиях Java из-за отказа от Security Manager[6].
Процесс аутентификации и авторизации
- Приложение создаёт экземпляр LoginContext[1].
- LoginContext, используя конфигурацию, загружает все связанные с приложением модули LoginModule.
- Приложение вызывает метод login у LoginContext.
- Метод login поочерёдно вызывает все LoginModule, которые осуществляют попытку аутентификации Subject. В случае успеха соответствующие Principal и Credential закрепляются за Subject — субъект считается аутентифицированным.
- LoginContext сообщает приложению об успешной или неудачной аутентификации.
- При успехе приложение получает объект Subject из LoginContext.
В связи с удалением Security Manager автоматическая проверка прав через файлы политик больше не осуществляется. После успешной аутентификации приложение должно самостоятельно проверять наличие необходимых Principal (ролей или прав) у полученного Subject[2][7].
Для выполнения кода от имени субъекта вместо устаревшего метода Subject.doAs теперь следует использовать Subject.callAs[8][7].
Примеры
Примечание: данный пример использует Policy и Security Manager, поддержка которых прекращена (JEP 411), поэтому в современных версиях Java этот код не будет выполнять принудительную проверку разрешений и приводится в исторических целях[4].
Приложение[9]:
package chp02;
import java.io.File; import java.io.IOException;
public class Chp02aMain {
public static void main(String[] args) throws IOException {
File file = new File("build/conf/cheese.txt");
try {
file.canWrite();
System.out.println("Можем записать в cheese.txt");
} catch (SecurityException e) {
System.out.println("Не можем записать в cheese.txt");
}
}
}
- Приложение создаёт объект файла cheese.txt.
- Проверяется наличие разрешения на запись.
- В зависимости от установленной политики либо осуществляется запись (выводится «Можем записать в cheese.txt»), либо выбрасывается исключение и выводится «Не можем записать в cheese.txt».
Файл конфигурации Policy:
grant
{
permission java.io.FilePermission "build/conf/cheese.txt", "write";
};
- Гарантируется разрешение FilePermission для записи в указанный файл.
Приложение[10]:
CallbackHandler handler = new RanchCallbackHandler(userName, password);
try {
LoginContext loginContext = new LoginContext("RanchLogin", handler);
// Вход
loginContext.login();
} catch (LoginException e) {
// Ошибка входа, не удалось аутентифицировать пользователя
e.printStackTrace();
}
- Приложение создаёт экземпляр LoginContext.
- Производится попытка входа в систему.
Файл конфигурации Login:
RanchLogin {
com.javaranch.auth.RanchLoginModule required;
};
- Этот файл задаёт, какой модуль LoginModule будет использоваться для аутентификации, и режим его работы.
- Здесь для имени "RanchLogin" назначен модуль RanchLoginModule, его запуск обязателен для аутентификации пользователя.
- Настраивается для Java виртуальной машины через параметр: -Djava.security.auth.login.config="JAAS_CONFIG_FILENAME"
Модуль RanchLoginModule:
public boolean login() throws LoginException {
boolean returnValue = true;
if (callbackHandler == null){
throw new LoginException("No callback handler supplied.");
}
Callback[] callbacks = new Callback[2];
callbacks[0] = new NameCallback("Username");
callbacks[1] = new PasswordCallback("Password", false);
try {
callbackHandler.handle(callbacks);
String userName = ((NameCallback) callbacks[0]).getName();
char [] passwordCharArray = ((PasswordCallback)
callbacks[1]).getPassword();
String password = new String(passwordCharArray);
//-->аутентификация производится по совпадению имени и пароля
returnValue = userName.equals(password);
} catch (IOException ioe) {
ioe.printStackTrace();
throw new LoginException("IOException occurred:
"+ioe.getMessage());
} catch (UnsupportedCallbackException ucbe) {
ucbe.printStackTrace();
throw new LoginException("UnsupportedCallbackException encountered:
"+ucbe.getMessage());
}
System.out.println("Вход выполнен");
return returnValue;
}
- С помощью Callback и CallbackHandler получают имя пользователя и пароль.
- Проверяется, совпадают ли имя и пароль.
- Возвращается значение true или false в зависимости от результата сравнения.
Конфигурационный файл Policy:
grant {
permission java.util.PropertyPermission "user", "read";
permission java.util.PropertyPermission "pass", "read";
permission java.util.PropertyPermission
"java.security.auth.login.config", "read";
permission java.util.PropertyPermission "java.security.policy", "read";
permission javax.security.auth.AuthPermission
"createLoginContext.RanchLogin";
};
- Настройка разрешений для создания объекта LoginContext и доступа к атрибутам.
- Требуется только при активном менеджере безопасности.
- Примечание: конфигурация Policy больше не применяется в современных версиях Java из-за удаления Security Manager.[2]
Результаты запуска приложения:
Одинаковые имя и пароль
java -Duser=rahul
-Dpass=rahul
-Djava.security.auth.login.config=jaas.config
-jar jaas-example.jar
- Вход выполнен успешно.
Различные имя и пароль
java -Duser=rahul
-Dpass=notrahul
-Djava.security.auth.login.config=jaas.config
-jar jaas-example.jar
- Имя и пароль не совпали — вход не выполнен.
Одинаковые имя и пароль + включён менеджер безопасности
java -Duser=rahul
-Dpass=rahul
-Djava.security.auth.login.config=jaas.config
-Djava.security.manager
-jar jaas-example.jar
- Не установлены необходимые права — операция доступа не может быть выполнена, вход не выполнен.
Одинаковые имя и пароль + включён менеджер безопасности + установлены права
java -Duser=rahul
-Dpass=rahul
-Djava.security.auth.login.config=jaas.config
-Djava.security.manager
-Djava.security.policy=policy.config
-jar jaas-example.jar
- Вход выполнен успешно.
- Примечание: запуск с флагом -Djava.security.manager устарел и в новых версиях JDK приведёт к ошибке или будет проигнорирован.
RanchLogin {
com.javaranch.auth.FirstLoginModule
requisite debug=true ;
com.javaranch.auth.SecondLoginModule
required debug=false email=admin@mydomain.com ;
};
Как видно из примера, для одной схемы аутентификации (RanchLogin) можно определить несколько LoginModule, каждый со своими параметрами (например, debug для FirstLoginModule, debug и email для SecondLoginModule). Различают несколько режимов загрузки модулей[10]:
- Required — модуль должен аутентифицировать пользователя, иначе осуществляется переход к следующему LoginModule.
- Requisite — при ошибке входа управление немедленно возвращается приложению, остальные LoginModule не вызываются.
- Sufficient — если модуль успешно выполнил аутентификацию, считается, что вход совершен, и управление возвращается приложению. Если нет, вызов переходят к следующим модулям.
- Optional — независимо от результата, всегда передаёт управление следующему LoginModule.
Помимо традиционной файловой конфигурации, в современных приложениях предпочтительным является программный подход с использованием метода Configuration.setConfiguration(). Это обеспечивает большую гибкость и независимость от внешних файлов, позволяя динамически управлять настройками непосредственно из кода приложения[11].
Современное состояние и альтернативы
Для новых проектов JAAS считается устаревающим. Стандартом де-факто для обеспечения безопасности стал фреймворк Spring Security, предоставляющий встроенную поддержку современных протоколов OAuth2, OpenID Connect (OIDC) и SAML[12]. В экосистеме Jakarta EE JAAS сохраняет роль фундаментального компонента, однако взаимодействие с ним происходит через более высокоуровневые API, такие как Jakarta Security и Jakarta Authentication[13].