Часть 1. Клиент на Angular
О чем эта статья
В этой статье, я расскажу как написать простую аутентификацию без помощи готовых решений для данной задачи. Она может быть полезна для новичков, которые хотят написать своё AAA (Authentication, Authorization, and Accounting). Репозиторий клиента на Angular и Репозиторий сервера на Spring.
В данной статье я сделаю выдержки кода клиентской части на Angular.
Клиент использующий сервер аутентифкикации
В этом разделе я опишу некоторые моменты разработки клиента, код которого можно переработать для взаимодействия с вашим сервером через REST API.
Посмотрим на структуру проекта:
.
+-- auth # Модуль аутентификации
¦ +-- actions
¦ ¦ L-- auth.ts
¦ +-- auth-routing.module.ts
¦ +-- auth.module.ts
¦ +-- components
¦ ¦ +-- signin.component.ts
¦ ¦ L-- signup.component.ts
¦ +-- containers
¦ ¦ +-- signin-page.component.ts
¦ ¦ L-- signup-page.component.ts
¦ L-- services
¦ L-- auth.service.ts
L-- core # Главный модуль
+-- components
¦ L-- index.component.ts
+-- containers
¦ +-- app.component.ts
¦ L-- not-found-page.ts
+-- core-routing.module.ts
+-- core.module.ts
+-- models
¦ +-- answer-message.ts
¦ +-- answer.ts # Ответ с сервера
¦ +-- notify-type.enum.ts
¦ +-- ping-payload.ts # Данные для отправки на сервер
¦ L-- pong-payload.ts # Данные для получения ответа с сервера
+-- reducers
¦ L-- reducer.reducer.ts
L-- services
+-- cookies.service.ts
+-- ping.service.ts # Сервисе выполняющий запросы пользователя на сервер
+-- security.service.ts # Сервис, в котором реализовано обращение к ААА API сервера
+-- services.module.ts
L-- utils.service.ts
REST клиент для сервис аутентификации/авторизации/регистрации
Для коммуникации с сервером будем использовать обретки для @angular/common/http/HttpClient
со следующей иерархией:
api-base
+-- api-security.service
L---- security.service
В них мы имеем вызовы API
сервера аутнетификации.
Далее, ответы от сервиса security.service
передаются ниже по иерархии в сервис auth.service
в нем сохраняется состояние пользователя в Store
и Cookies
. Cookies
используются для восстановления состояния аутнетифицированного пользователя после перезагрузки страницы. И в случае ошибок они обрабатываются путем вывода сообщения в консоль и, затем, передаются вверх по иерархии.
Приведу пример метода аутентификации:
authorize(credentials: UserCredentials): Observable<AuthUser | Failure> {
return this.securityService.authorize(credentials) // Делаем HTTP запрос на сервер
.pipe(
tap(authUser => this.setAuthUserState(authUser)), // Сохраняем состояние пользователя
catchError(error => { // Ловим ошибки
return this.errorHandler.handleAuthError(error);
})
)
}
Полезная нагрузка на сервер
Для отправки запросов на сервер используется обёртка к HttpClient
— ping.service
, в которой вызывается метод ping
API
сервера на Spring.
Прерыватель запросов (api-interceptor)
Для отправки данных об аутентифицированном пользователе используются прерыватель HTTP запросов, который кладёт в заголовки AccessToken и UserSession.
Приведу пример кода:
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return this.authService.getLoggedUser() // Берём текущего залогиненного пользователя
.pipe(
map(authUser => {
// Добавляем данные в заголовки
let clonedRequest = req.clone();
let isLoggedIn = UtilsService.isLoggedIn(authUser);
if (isLoggedIn) {
clonedRequest = clonedRequest.clone(
{
headers:
clonedRequest.headers
.append(AppConstants.ACCESS_TOKEN_HEADER, authUser.accessToken)
.append(AppConstants.USER_SESSION_HEADER, authUser.userSession)
}
);
}
return next.handle(clonedRequest);
}),
switchMap(value => {
return value;
}),
);
}
Схема взаимодействия с сервером
На диаграмм последовательностей (Sequence Diagram) "Диаграмма последовательности взаимодействия с сервером" изображён процесс выполнения запросов к серверу. После загрузки страницы, клиент отправляет запрос на аутентификацию (здесь рассмотрен случай с пройденной аутентификацией без ошибок). Далее, после получения этого запроса, сервер выдаёт токен. Нужно уточнить, что токен выдаётся не просто так, а после авторизации, которая для упрощения схемы не показана. Подробности по авторизации можно прочитать в смежной статье. Затем, после генерации токена на сервере, возвращается ответ клиенту. Клиент сохраняет этот токен и выполняет ping
запрос к серверу. Сервер проверяет пришедший токен, обрабатывает данные ping
запроса и генерирует новый токен. В нашем примере, он просто возвращает строку "${data.getPing()} from ${authUser.getUsername()} and PONG from server"
. После получения ответа с сервера, клиент сохраняет токен и выполняет с этим новым токеном следующий запрос.
Если токен потеряется или клиент его не правильно сохранит, то этот и следующие запросы не пройдут до тех пор, пока пользователь не авторизуется повторно.
Заключение
В этой статье мы рассмотрели как сделать простой клиент аутентификации с помощью Angular, который упрощает работу с данной связкой.
via-site
Interceptor — это не 'прерыватель' а 'перехватчик'