«1. Введение
По умолчанию приложения JHipster используют локальное хранилище данных для хранения имен пользователей и паролей. Однако во многих реальных сценариях может оказаться желательным использовать существующую внешнюю службу для проверки подлинности.
В этом руководстве мы рассмотрим, как использовать внешний сервис для аутентификации в JHipster. Это может быть любая известная служба, такая как LDAP, вход через социальные сети или любая произвольная служба, которая принимает имя пользователя и пароль.
2. Аутентификация в JHipster
JHipster использует Spring Security для аутентификации. Класс AuthenticationManager отвечает за проверку имени пользователя и пароля.
AuthenticationManager по умолчанию в JHipster просто сверяет имя пользователя и пароль с локальным хранилищем данных. Это может быть MySQL, PostgreSQL, MongoDB или любые альтернативы, поддерживаемые JHipster.
Важно отметить, что AuthenticationManager используется только для первоначального входа в систему. После аутентификации пользователя он получает веб-токен JSON (JWT), который используется для последующих вызовов API.
2.1. Изменение аутентификации в JHipster
Но что, если у нас уже есть хранилище данных, содержащее имена пользователей и пароли, или служба, которая выполняет аутентификацию за нас?
Чтобы обеспечить пользовательскую схему аутентификации, мы просто создаем новый bean-компонент типа AuthenticationManager. Это будет иметь приоритет над реализацией по умолчанию.
Ниже приведен пример, показывающий, как создать собственный AuthenticationManager. У него есть только один метод для реализации:
public class CustomAuthenticationManager implements AuthenticationManager {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
try {
ResponseEntity<LoginResponse> response =
restTemplate.postForEntity(REMOTE_LOGIN_URL, loginRequest, LoginResponse.class);
if(response.getStatusCode().is2xxSuccessful()) {
String login = authentication.getPrincipal().toString();
User user = userService.getUserWithAuthoritiesByLogin(login)
.orElseGet(() -> userService.createUser(
createUserDTO(response.getBody(), authentication)));
return createAuthentication(authentication, user);
}
else {
throw new BadCredentialsException("Invalid username or password");
}
}
catch (Exception e) {
throw new AuthenticationServiceException("Failed to login", e);
}
}
}
В этом примере мы передаем имя пользователя и учетные данные из объекта аутентификации во внешний API.
Если вызов успешен, мы возвращаем новый UsernamePasswordAuthenticationToken, чтобы указать на успех. Обратите внимание, что мы также создаем локальную запись пользователя, которую мы обсудим позже.
Если вызов завершается неудачей, мы выбрасываем некоторый вариант AuthenticationException, чтобы Spring Security изящно отступила за нас.
Этот пример намеренно упрощен, чтобы показать основы пользовательской аутентификации. Однако он может выполнять более сложные операции, такие как привязка и аутентификация LDAP или использовать OAuth.
3. Другие соображения
До сих пор мы фокусировались на потоке аутентификации в JHipster. Но есть несколько других областей нашего приложения JHipster, которые мы должны изменить.
3.1. Интерфейсный код
Код JHipster по умолчанию реализует следующий процесс регистрации и активации пользователя:
-
Пользователь регистрирует учетную запись, используя свою электронную почту и другие необходимые данные. JHipster создает учетную запись и устанавливает ее как неактивную, а затем отправляет электронная почта новому пользователю со ссылкой для активации. После перехода по ссылке учетная запись пользователя помечается как активная
Существует аналогичный процесс для сброса пароля.
Все это имеет смысл, когда JHipster управляет учетными записями пользователей. Но они не требуются, когда мы полагаемся на внешнюю службу для аутентификации.
Поэтому нам необходимо принять меры, чтобы эти функции управления учетной записью были недоступны для пользователя.
Это означает удаление их из кода Angular или React, в зависимости от того, какой фреймворк используется в приложении JHipster.
Используя Angular в качестве примера, приглашение для входа по умолчанию включает ссылки для сброса пароля и регистрации. Мы должны удалить их из app/shared/login/login.component.html:
<div class="alert alert-warning">
<a class="alert-link" (click)="requestResetPassword()">Did you forget your password?</a>
</div>
<div class="alert alert-warning">
<span>You don't have an account yet?</span>
<a class="alert-link" (click)="register()">Register a new account</a>
</div>
Мы также должны удалить ненужные элементы меню навигации из app/layouts/navbar/navbar.component.html:
<li *ngSwitchCase="true">
<a class="dropdown-item" routerLink="password" routerLinkActive="active" (click)="collapseNavbar()">
<fa-icon icon="clock" fixedWidth="true"></fa-icon>
<span>Password</span>
</a>
</li>
~~ ~ и
<li *ngSwitchCase="false">
<a class="dropdown-item" routerLink="register" routerLinkActive="active" (click)="collapseNavbar()">
<fa-icon icon="user-plus" fixedWidth="true"></fa-icon>
<span>Register</span>
</a>
</li>
Несмотря на то, что мы удалили все ссылки, пользователь по-прежнему мог вручную переходить на эти страницы. Последний шаг — удалить неиспользуемые маршруты Angular из app/account/account.route.ts.
После этого должен остаться только маршрут настроек:
import { settingsRoute } from './';
const ACCOUNT_ROUTES = [settingsRoute];
3.2. Java API
В большинстве случаев достаточно просто удалить интерфейсный код управления учетной записью. Однако, чтобы быть абсолютно уверенным, что код управления учетной записью не вызывается, мы также можем заблокировать связанные API Java.
«Самый быстрый способ сделать это — обновить класс SecurityConfiguration, чтобы запретить все запросы к связанным URL-адресам:
.antMatchers("/api/register").denyAll()
.antMatchers("/api/activate").denyAll()
.antMatchers("/api/account/reset-password/init").denyAll()
.antMatchers("/api/account/reset-password/finish").denyAll()
Это предотвратит любой удаленный доступ к API без необходимости удаления какого-либо кода.
3.3. Шаблоны электронной почты
Приложения JHipster поставляются с набором шаблонов электронной почты по умолчанию для регистрации учетной записи, активации и сброса пароля. Предыдущие шаги эффективно предотвратят отправку электронных писем по умолчанию, но в некоторых случаях мы можем захотеть использовать их повторно.
Например, мы можем захотеть отправить приветственное письмо, когда пользователь входит в систему в первый раз. Шаблон по умолчанию включает шаги для активации учетной записи, поэтому мы должны изменить его.
Все шаблоны писем находятся в resources/templates/mail. Это файлы HTML, которые используют Thymeleaf для передачи данных из кода Java в электронные письма.
Все, что нам нужно сделать, это отредактировать шаблон, включив в него нужный текст и макет, а затем использовать MailService для его отправки.
3.4. Роли
Когда мы создаем локальную запись пользователя JHipster, мы также должны позаботиться о том, чтобы у нее была хотя бы одна роль. Обычно для новых учетных записей достаточно роли ПОЛЬЗОВАТЕЛЯ по умолчанию.
Если внешняя служба предоставляет собственное сопоставление ролей, у нас есть два дополнительных шага:
- Ensure any custom roles exist in JHipster
- Update our custom AuthenticationManager to set the custom roles when creating new users
JHipster также предоставляет интерфейс управления для добавления и удаления ролей для пользователей.
3.5. Удаление учетной записи
Стоит отметить, что JHipster также предоставляет представление управления удалением учетной записи и API. Это представление доступно только для пользователей с правами администратора.
Мы могли бы удалить и ограничить этот код, как мы это сделали для регистрации учетной записи и сброса пароля, но на самом деле это не обязательно. Наш пользовательский AuthenticationManager всегда будет создавать новую запись учетной записи, когда кто-то входит в систему, поэтому удаление учетной записи на самом деле мало что дает.
4. Заключение
В этом руководстве мы увидели, как заменить код аутентификации JHipster по умолчанию нашей собственной схемой аутентификации. Это может быть LDAP, OIDC или любая другая служба, которая принимает имя пользователя и пароль.
Мы также видели, что использование внешней службы аутентификации также требует некоторых изменений в других областях нашего приложения JHipster. Это включает в себя интерфейсные представления, API и многое другое.
Как всегда, пример кода из этого руководства доступен на GitHub.