3.2.11.2. Вход в систему
Платформа предоставляет встроенные механизмы аутентификации, функциональность которых может быть расширена в приложениях. Они включают в себя различные схемы аутентификации, такие как вход по паролю, функциональность "Запомнить меня", доверенный и анонимный вход в систему.
Данный раздел преимущественно описывает механизмы аутентификации среднего слоя. Для информации об аутентификации веб-клиента см. Процесс входа в Web Client.
Платформа включает следующие механизмы среднего слоя:
-
AuthenticationManagerреализованный классомAuthenticationManagerBean -
Реализации интерфейса
AuthenticationProvider -
AuthenticationServiceреализованный классомAuthenticationServiceBean -
UserSessionLog- см. журналирование пользовательских сессий.
Также, платформа включает следующие дополнительные компоненты:
-
TrustedClientServiceреализованный классомTrustedClientServiceBean- предоставляет анонимную/системную сессию для доверенных приложений-клиентов. -
AnonymousSessionHolder- создаёт и хранит анонимную сессю для доверенных приложений-клиентов. -
UserCredentialsChecker- проверяет, могут ли быть использованы переданныеCredentials, например, для защиты от атак типа brute-force. -
UserAccessChecker- проверяет, может ли пользователь выполнять вход из данного контекста, например, в REST API или с указанного IP адреса.
Основной интерфейс аутентификации - AuthenticationManager, включающий 4 метода:
public interface AuthenticationManager {
AuthenticationDetails authenticate(Credentials credentials) throws LoginException;
AuthenticationDetails login(Credentials credentials) throws LoginException;
UserSession substituteUser(User substitutedUser);
void logout();
}
Здесь есть два метода с похожей ответственностью: authenticate() и login(). Оба метода проверяют, являются ли переданные аутентификационные данные валидными и соответствуют ли они активному пользователю системы, затем возвращают объект AuthenticationDetails. Основное отличие в работе этих методов в том, что метод login() активирует сессию пользователя, так что она может использоваться в дальнейшем для вызова сервисов.
Объект Credentials представляет собой набор аутентификационных данных. Платформа поставляет несколько типов аутентификационных данных:
Доступные на всех слоях:
-
LoginPasswordCredentials -
RememberMeCredentials -
TrustedClientCredentials
Доступные только на среднем слое:
-
SystemUserCredentials -
AnonymousUserCredentials
Методы login / authenticate AuthenticationManager возвращают объект AuthenticationDetails, который содержит объект UserSession. Этот объект может быть использован для проверки дополнительных разрешений, чтения свойств объекта User и атрибутов сессии. В платформе есть встроенная реализация интерфейса AuthenticationDetails - SimpleAuthenticationDetails, который хранит только объект сессии пользователя, приложения могут предоставлять свою реализацию AuthenticationDetails с дополнительной инфомацией для приложений-клиентов.
AuthenticationManager может выполнить метод authenticate() с одним из следующих результатов:
-
вернуть объект
AuthenticationDetailsесли он подтверждает, что переданные аутентификационные данные верны и соответствуют активному пользователю системы. -
выбросить
LoginExceptionесли невозможно аутентифицировать пользователя, неверны аутентификационные данные или если пользователю запрещён доступ к системе. -
выбросить
UnsupportedCredentialsExceptionесли переданный тип аутентификационных данных не поддерживается системой.
Реализация AuthenticationManager по умолчанию - AuthenticationManagerBean, который делегирует аутентификацию цепочке экземпляров AuthenticationProvider. AuthenticationProvider - это модуль аутентификации, который может обрабатывать объекты Credentials определённого типа. AuthenticationProvider также имеет специальный метод supports() позволяющий узнать поддерживается ли переданный тип аутентификационных данных.
Стандартный процесс входа пользователя:
-
пользователь вводит свой логин и пароль
-
клиентский блок приложения вызывает метод
Connection.login(), передавая ему логин пользователя и пароль. -
Connectionсоздаёт объектCredentialsи вызывает методlogin()сервисаAuthenticationService. -
AuthenticationServiceделегирует выполнение бинуAuthenticationManager, который использует цепочку объектовAuthenticationProvider. В этой цепочке имеется бинLoginPasswordAuthenticationProvider, поддерживающий аутентификационные данные типаLoginPasswordCredentials. Он загружает объектUserпо полученному логину, хэширует полученный хэш пароля повторно, используя в качестве соли идентификатор пользователя, и сравнивает полученный хэш с сохраненным в БД хэшем пароля. В случае несовпадения выбрасывается исключениеLoginException. -
После успешной аутентификации в созданный экземпляр UserSession загружаются все параметры доступа данного пользователя: список ролей, права, ограничения и атрибуты сессии.
-
Если журналирование пользовательских сессий активировано, в базу данных сохраняется запись с информацией о текущей сессии.
См. также Специфика процесса входа в Web Client.
Алгоритм хэширования паролей реализуется бином типа EncryptionModule и задается в свойстве приложения cuba.passwordEncryptionModule. По умолчанию - BCrypt.
- Встроенные провайдеры аутентификации
-
Платформа включает следующие реализации интерфейса
AuthenticationProvider:-
LoginPasswordAuthenticationProvider -
RememberMeAuthenticationProvider -
TrustedClientAuthenticationProvider -
SystemAuthenticationProvider -
AnonymousAuthenticationProvider
Все реализации загружают пользователя из базы данных, проверяют переданные аутентификационные данные и создают неактивный экземпляр пользовательской сессии при помощи
UserSessionManager. Этот экземпляр может стать активным позднее, если вызывается методAuthenticationManager.login().Бины
LoginPasswordAuthenticationProvider,RememberMeAuthenticationProviderиTrustedClientAuthenticationProviderиспользуют дополнительные подключаемые проверки: бины, реализующие интерфейсUserAccessChecker. Если по крайней мере одна из таких проверок выбрасывает исключениеLoginException, то аутентификация не выполняется и исключениеLoginExceptionпередаётся вызывающему коду.Кроме того,
LoginPasswordAuthenticationProviderиRememberMeAuthenticationProviderпроверяют аутентификационные данные при помощи бинов UserCredentialsChecker. Имеется одна встроенная реализация этого интерфейса - BruteForceUserCredentialsChecker, который проверяет, пытается ли пользователь подобрать верные аутентификационные данные при помощи атаки brute-force. -
- Исключения
-
AuthenticationManagerиAuthenticationProviderмогут выбрасывать исключение LoginException или одного из его наследников из методовauthenticate()иlogin(). Исключение UnsupportedCredentialsException выбрасывается, если переданный объект аутентификационных данныхCredentialsне может быть обработан имеющимисяAuthenticationProvider.См. следующие классы исключений:
-
UnsupportedCredentialsException -
LoginException -
AccountLockedException -
UserIpRestrictedException -
RestApiAccessDeniedException
-
- События
-
Стандартная реализация
AuthenticationManager-AuthenticationManagerBeanпубликует следующие события во время аутентификации / входа пользователей:-
BeforeAuthenticationEvent/AfterAuthenticationEvent -
BeforeLoginEvent/AfterLoginEvent -
AuthenticationSuccessEvent/AuthenticationFailureEvent -
UserLoggedInEvent/UserLoggedOutEvent -
UserSubstitutedEvent
Бины Spring на среднем слое приложения могут обрабатывать эти события при помощи механизма подписок
@EventListener:@Component public class LoginEventListener { @Inject private Logger log; @EventListener protected void onUserLoggedIn(UserLoggedInEvent event) { User user = event.getSource().getUser(); log.info("Logged in user {}", user.getInstanceName()); } }Обработчики всех событий, перечисленных выше (кроме
AfterLoginEvent,UserSubstitutedEventиUserLoggedInEvent), могут выброситьLoginException, чтобы прервать процесс аутентификации / входа пользователя.Например, мы можем реализовать механизм режима обслуживания системы, который позволит запретить вход в систему на время её обслуживания.
@Component public class MaintenanceModeValve { private volatile boolean maintenance = true; public boolean isMaintenance() { return maintenance; } public void setMaintenance(boolean maintenance) { this.maintenance = maintenance; } @EventListener protected void onBeforeLogin(BeforeLoginEvent event) throws LoginException { if (maintenance && event.getCredentials() instanceof AbstractClientCredentials) { throw new LoginException("Sorry, system is unavailable"); } } } -
- Точки расширения
-
Вы можете расширить механизм аутентификации, используя следующие точки расширения:
-
AuthenticationService- заменить существующийAuthenticationServiceBean. -
AuthenticationManager- заменить существующийAuthenticationManagerBean. -
AuthenticationProvider- реализовать новый или заменить существующий бинAuthenticationProvider. -
Events - реализовать обработчик одного из доступных событий.
Вы можете заменить существующие бины, задействуя механизмы Spring Framework, например, зарегистрировав новую реализацию в XML конфигурации Spring модуля core:
<bean id="cuba_LoginPasswordAuthenticationProvider" class="com.company.authext.core.CustomLoginPasswordAuthenticationProvider"/>public class CustomLoginPasswordAuthenticationProvider extends LoginPasswordAuthenticationProvider { @Inject public CustomLoginPasswordAuthenticationProvider(Persistence persistence, Messages messages) { super(persistence, messages); } @Override public AuthenticationDetails authenticate(Credentials credentials) throws LoginException { LoginPasswordCredentials loginPassword = (LoginPasswordCredentials) credentials; // for instance, add new check before login if ("demo".equals(loginPassword.getLogin())) { throw new LoginException("Demo account is disabled"); } return super.authenticate(credentials); } }Обработчики событий могут быть упорядочены при помощи аннотации
@Order. Все бины и обработчики событий платформы используют значениеorderиз диапазона 100 и 1000, что позволяет добавлять обработчики на уровне проект как до, так и после кода платформы. Если вы хотите добавить свой обработчик до обработчиков/бинов платформы - используйте значение меньше 100.Задание
orderдля обработчика события:@Component public class DemoEventListener { @Inject private Logger log; @Order(10) @EventListener protected void onUserLoggedIn(UserLoggedInEvent event) { log.info("Demo"); } }Бины
AuthenticationProviderмогут реализовать интерфейс Ordered и методgetOrder()для определения очерёдности исполнения.@Component public class DemoAuthenticationProvider extends AbstractAuthenticationProvider implements AuthenticationProvider, Ordered { @Inject private UserSessionManager userSessionManager; @Inject public DemoAuthenticationProvider(Persistence persistence, Messages messages) { super(persistence, messages); } @Nullable @Override public AuthenticationDetails authenticate(Credentials credentials) throws LoginException { // ... } @Override public boolean supports(Class<?> credentialsClass) { return LoginPasswordCredentials.class.isAssignableFrom(credentialsClass); } @Override public int getOrder() { return 10; } } -
- Дополнительные возможности
-
-
В платформе имеется механизм защиты от взлома пароля методом перебора. Для его включения необходимо установить свойство приложения cuba.bruteForceProtection.enabled для блока Middleware. В этом случае после определенного количества неуспешных попыток входа для определенного имени пользователя с определенного IP-адреса вход для пары логин + IP-адрес блокируется на некоторое время. Допустимое количество попыток входа для пары логин + IP-адрес определяется свойством приложения cuba.bruteForceProtection.maxLoginAttemptsNumber (по умолчанию 5). Интервал блокировки пользователя в секундах задается свойством cuba.bruteForceProtection.blockIntervalSec (по умолчанию 60).
-
Возможен вариант, когда пароль пользователя (точнее, хэш пароля) не хранится в базе данных, а проверяется внешними средствами, например, путем интеграции с ActiveDirectory. В этом случае фактически аутентификацию выполняет клиентский блок, а Middleware "доверяет" клиенту, создавая сессию по одному только логину пользователя без пароля методом
LoginService.loginTrusted(). МетодloginTrusted()требует выполнения следующих условий:-
клиентский блок должен передать так называемый доверенный пароль, задаваемый на Middleware и на клиентском блоке свойством приложения cuba.trustedClientPassword
-
IP-адрес клиентского блока должен быть в списке, задаваемом свойством приложения cuba.trustedClientPermittedIpList
-
-
Вход в систему требуется также для автоматических процессов, запускаемых по расписанию, а также при подключении к бинам Middleware через JMX-интерфейс. Строго говоря, такие действия считаются административными и не требуют аутентификации до тех пор, пока не выполняется каких-либо изменений сущностей в базе данных. При записи сущностей в БД требуется проставить логин пользователя, который выполнил изменения, поэтому для работы таких процессов должен быть указан пользователь, от лица которого выполняются изменения.
Дополнительным плюсом входа в систему для автоматического процесса и для JMX-вызова является то, что вывод в журнал сообщений от логгеров сопровождается указанием логина текущего пользователя, если пользовательская сессия установлена в потоке выполнения. Это упрощает поиск сообщений от конкретного процесса при разборе журнала.
Вход в систему для процессов внутри Middleware выполняется вызовом
AuthenticationManager.login()с передачей объектаSystemUserCredentials, содержащего логин пользователя (без пароля), от имени которого будет работать данный процесс. В результате создается объект UserSession, который будет закэширован в данном блоке Middleware и не будет реплицироваться в кластере.
Более подробно аутентификация процессов внутри Middleware рассмотрена в разделе Системная аутентификация.
-
- Устаревшие механизмы
-
Следующие компоненты считаются устаревшими:
-
LoginServiceделегирует исполнение методов сервисуAuthenticationService -
LoginWorkerделегирует исполнение методов бинуAuthenticationManager
Не используйте эти компоненты в коде приложений. Они будут удалены в одной из следующих версий платформы.
-