меню

Keycloak является оптимальным выбором для реализации механизмов аутентификации и авторизации в Angular-приложении. Ниже представлено пошаговое руководство по интеграции Keycloak с Angular.

В современных условиях динамичной разработки, где безопасность играет ключевую роль, интеграция системы управления идентификацией (IAM) с фронтенд-слоем — частью решения, которую видят и с которой взаимодействуют пользователи — становится неотъемлемым элементом ИТ-проектов.

Одним из признанных решений в мире программирования является Keycloak — open-source-система для управления идентификацией и доступом (IAM). Она не только предлагает готовый набор мощных инструментов для работы с аутентификацией и авторизацией, но и позволяет гибко настраивать функционал в соответствии с индивидуальными требованиями проекта.

В этой статье мы покажем, как интегрировать Keycloak с фронтенд-частью приложения на Angular. Мы разберем необходимые шаги для успешного подключения системы к вашему фронтенду и рассмотрим преимущества такой интеграции — в том числе новые возможности, появившиеся в последних версиях Keycloak. Также мы дадим рекомендации по настройке и приведем практические примеры реализации, чтобы помочь разработчикам эффективно защитить свои приложения, сохранив при этом удобный и дружелюбный интерфейс для пользователей.

Что такое Keycloak, каковы его преимущества?

Keycloak — это мощное open-source-решение, предоставляющее функционал для аутентификации, авторизации, федеративной идентификации (federated identity), управления сеансами (session management) и многих других сложных аспектов безопасности. Это делает его идеальным выбором для проектов, требующих защищённой и интегрированной среды.

Keycloak предлагает множество способов быстрой и простой настройки, что позволяет удовлетворить потребности самых разных пользователей. Платформа поддерживает множество стандартных протоколов, включая OpenID Connect, OAuth 2.0 и SAML 2.0, обеспечивая совместимость с различными приложениями. Такой подход позволяет реализовать функционал единого входа (SSO, Single Sign-On) независимо от архитектуры вашего решения.

Keycloak с поддержкой контейнеризации представляет собой идеальное решение для контейнерных сред, таких как Docker и Kubernetes. Это подтверждается следующими возможностями платформы:

  • Официальные Docker-образы — команда Keycloak предоставляет и поддерживает официальные образы Docker, гарантируя их актуальность и безопасность.
  • Конфигурация через переменные окружения — позволяет настраивать различные параметры (БД, SSL-соединения, конфигурацию realm и др.) без изменения конфигурационных файлов.
  • Интеграция с системами оркестрации — Keycloak легко интегрируется с платформами оркестрации, такими как Kubernetes, и может использоваться в средах, управляемых оркестратором. Это обеспечивает динамическое управление экземплярами Keycloak, простое масштабирование в зависимости от нагрузки и использование преимуществ контейнерной оркестрации.
  • Подробная документация — Keycloak также предоставляет исчерпывающую документацию по настройке и развертыванию в контейнерах, что упрощает понимание возможностей интеграции с такими инструментами, как Podman, Docker, Kubernetes и OpenShift.

Благодаря этому Keycloak стал одним из самых популярных решений для управления идентификацией и доступом (IAM).

Конфигурирование проекта.

Чтобы продемонстрировать преимущества использования Keycloak, мы развернём сервер и интегрируем его с простым фронтенд-приложением на Angular 17 версии.

Keycloak потребует настройки, но благодаря детальной пошаговой документации этот процесс достаточно прост. В рамках данной статьи мы не будем его описывать, так как наш сервер используется только для иллюстрации рассматриваемых концепций без необходимости расширенной конфигурации.

В рамках данного руководства рассматривается интеграция с Angular 17-приложением. Первым шагом представим библиотеки для работы с Keycloak. В нашем проекте применялись две ключевые библиотеки:

  • keycloak-js — ключевой компонент экосистемы Keycloak, предназначенный для защиты веб-приложений с использованием протокола OpenID Connect. Эта клиентская JavaScript-библиотека обеспечивает аутентификацию и авторизацию пользователей в веб-приложениях. Благодаря гибкости и простоте интеграции, keycloak-js активно используется во множестве проектов, что подтверждается статистикой npm-регистра. Регулярные обновления библиотеки свидетельствуют о активной поддержке со стороны сообщества. Кроме того, библиотека имеет встроенную поддержку приложений Cordova, что расширяет её применение на мобильные приложения.
  • keycloak-angular — библиотека-обёртка, упрощающая использование keycloak-js в Angular-приложениях. Она расширяет функциональность базовой библиотеки, добавляя новые методы для более удобной интеграции с Angular. Также предоставляет базовую реализацию AuthGuard, позволяющую кастомизировать логику аутентификации. Дополнительно доступна возможность использования HttpClient Interceptor, который автоматически добавляет Authorization-заголовок к указанным HTTP-запросам.

Требования нашего проекта.

Для данной реализации необходимо:

  • Реализовать систему входа/выхода через Keycloak;
  • Обеспечить базовую интеграцию с Keycloak.

Основные сценарии работы:

  • Разграничение доступа к страницам;
  • Авторизация через Keycloak;
  • Получение данных пользователя;
  • Защита маршрутов;
  • Автоматический редирект на логин;
  • Страница подтверждения выхода.

Инициализация приложения и настройка Keycloak.

Для начала работы загрузите образ Keycloak для Docker с официального сайта. Чтобы запустить Keycloak в локальном окружении, просто выполните следующую команду:

	
docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:23.0.4 start-dev
	

После выполнения этой команды вы можете перейти в панель управления Keycloak по адресу http://localhost:8080. Для выполнения необходимой настройки следуйте инструкциям на официальном сайте Keycloak. После внесения всех изменений оставьте процесс Keycloak работающим в консоли - это обеспечит возможность взаимодействия с сервером.

В рамках этого руководства мы использовали realm Pretius-Keycloak-FE-Integration с client id keycloak-angular-integration-tutorial.

Для фронтенд-приложения необходимо создать новый Angular-проект на базе фреймворка Angular 17. Для этого выполните следующие действия:

	
ng new keycloak-fe-integration
	

Далее система запросит информацию о функциях, которые необходимо включить в начальную конфигурацию. Выберите подходящие опции в соответствии с вашими требованиями или оставьте значения по умолчанию.

В нашем проекте используются следующие версии библиотек:

  • keycloak-js — версия 22;
  • keycloak-angular — версия 15.
	
npm install keycloak-js@22 keycloak-angular@15 --save
	

После установки необходимых npm-пакетов и других зависимостей, требуемых Angular, вы увидите рабочее пространство с корневой директорией под названием keycloak-fe-integration (если вы использовали такое же имя, как у нас). Следующим шагом будет замена существующей файловой структуры на указанную ниже:

Angular Keykloak файловая структура

Все страницы структурированы в директории pages по функциональному признаку. Прежде чем анализировать бизнес-логику, рассмотрим состав данной папки, содержащей компоненты страниц приложения.

Стартовая страница (main-page), открываемая при первом запуске, требует особого внимания. Перейдём к анализу её логики:

	
@Component({
  selector: 'app-main-page',
  standalone: true,
  imports: [NgIf],
  styleUrl: './main-page.component.scss',
  template: `<div class="content">
                <span class="title">Main page</span>
             </div>
             <a
                *ngIf="!isLoggedIn"
                role="button"
                class="button"
                (click)="redirectToLoginPage()"
                >
              Log In
              </a>`,
})
export class MainPageComponent {
  get isLoggedIn(): boolean {
    return this.authenticationService.isLoggedIn();
  }
  constructor(private readonly authenticationService: AuthenticationService) {}
  redirectToLoginPage(): void {
    this.authenticationService.redirectToLoginPage();
  }
}
	

Данный компонент выполняет только две функции:

  • Отображает в заголовке информацию о текущем местоположении пользователя;
  • Предоставляет кнопку для входа через Keycloak.

Следующий компонент для анализа - unprotected-route:

	
@Component({
  selector: 'app-unprotected-route',
  standalone: true,
  imports: [CommonModule],
  template: `
  <div class="content">
    <span class="title">Welcome {{ username }}!</span>
    <span class="status-text">This is an unprotected route</span>
  </div>
`
})
export class UnprotectedRouteComponent {
  get username(): string {
    return this.authenticationService.isLoggedIn()
      ? this.authenticationService.userName
      : 'friend';
  }
  constructor(readonly authenticationService: AuthenticationService) {}
}
	

Как видите, этот компонент отвечает только за отображение имени пользователя при его авторизации. В противном случае выводится стандартная строка-заглушка.

При анализе компонента protected-route становится очевидным, что его функциональность ограничена выводом базовых данных.

	
@Component({
  selector: 'app-protected-route',
  standalone: true,
  template: `
  <div class="content">
    <span class="title">Welcome {{ authenticationService.userName }}!</span>
    <span class="status-text">This is a protected route</span>
  </div>
`
})
export class ProtectedRouteComponent {
  constructor(readonly authenticationService: AuthenticationService) {}
}
	

Кроме того, в последних двух компонентах содержится лишь простой код, отвечающий за отображение чистого HTML. Ниже представлено содержимое папки not-found:

	
@Component({
  selector: 'app-not-found',
  standalone: true,
  template: `<h1>Page not found</h1>`
})
export class NotFoundComponent {}
	

А вот экран выхода из системы, который отображается при выходе пользователя из приложения:

	
@Component({
  selector: 'app-logout-screen',
  standalone: true,
  template: `
  <div class="content">
    <span class="title">You have been logged out!</span>
  </div>`
})
export class LogoutScreenComponent {}
	

Анализ представленных фрагментов кода показывает, что отдельные страницы не участвуют в обмене данными между Keycloak и клиентской частью. В соответствии с принципами использованными при создании данного руководства, бизнес-логика вынесена из компонентов и реализована в:

  • Системе защиты доступа (Guards);
  • Специальном сервисе для взаимодействия с Keycloak.

В следующих разделах будут рассмотрены ключевые компоненты системы и их функции.

Настройка Keycloak.

После успешной установки библиотеки keycloak-angular можно приступить к настройке модуля Keycloak во frontend-приложении.

  • Определение переменных окружения. Сначала необходимо задать переменные окружения, используемые в приложении. Для этого создайте файл (например, environment.ts) в корневой директории проекта.
  • Пример конфигурации. Ниже приведены переменные, которые использовались в нашем проекте:
 	
export const environment = {
  production: false,
  keycloak: {
    authority: 'http://localhost:8080',
    redirectUri: 'http://localhost:4200',
    postLogoutRedirectUri: 'http://localhost:4200/logout',
    realm: 'Pretius-Keycloak-FE-Integration',
    clientId: 'keycloak-angular-integration-tutorial',
  },
  idleConfig: { idle: 10, timeout: 60, ping: 10 },
};
 	
 

Данные переменные хранят конфигурационные параметры сервера Keycloak, используемые для подключения Angular-приложения. Их детальное описание представлено в следующих разделах.

Чтобы гарантировать инициализацию Keycloak во время запуска приложения:

  1. Добавьте провайдер с токеном APP_INITIALIZER;
  2. Реализуйте функцию инициализации для выполнения в runtime;
  3. Зарегистрируйте её в корневом модуле (AppModule).

Данный подход позволяет выполнять настройку Keycloak с требуемой конфигурацией. В нашей реализации использовалась модульная архитектура. Ниже представлено содержимое соответствующего модуля:

 	
export const initializeKeycloak = (keycloak: KeycloakService) => async () =>
  keycloak.init({
    config: {
      url: environment.keycloak.authority,
      realm: environment.keycloak.realm,
      clientId: environment.keycloak.clientId,
    },
    loadUserProfileAtStartUp: true,
    initOptions: {
      onLoad: 'check-sso',
      silentCheckSsoRedirectUri:
        window.location.origin + '/assets/silent-check-sso.html',
      checkLoginIframe: false,
      redirectUri: environment.keycloak.redirectUri,
    },
  });
@NgModule({
  bootstrap: [AppComponent],
  declarations: [AppComponent],
  imports: [
    RouterModule.forRoot(routes),
    BrowserModule,
    KeycloakAngularModule,
    CommonModule,
    HttpClientModule,
    NavigationBarComponent,
    FooterComponent,
  ],
  providers: [
    {
      provide: APP_INITIALIZER,
      useFactory: initializeKeycloak,
      multi: true,
      deps: [KeycloakService],
    },
    provideUserIdleConfig({
      idle: environment.idleConfig.idle,
      ping: environment.idleConfig.ping,
      timeout: environment.idleConfig.timeout,
    }),
  ],
})
export class AppModule {}
 	

Данный код позволяет настроить поддержку Keycloak в приложении. Ключевой здесь является функция initializeKeycloak(), которая отвечает за корректную инициализацию сервиса. Необходимо убедиться, что передаваемые в неё данные точно соответствуют конфигурации в Keycloak. Для этого укажите следующие значения в config-объекте:

  • url - путь к нашему серверу Keycloak (по умолчанию localhost:8080);
  • realm - имя созданного в Keycloak realm'а;
  • clientId - название клиента, назначенного нашему приложению в Keycloak, который будет выполнять запросы аутентификации пользователей.

Также необходимо установить параметр loadUserProfileAtStartUp, чтобы приложение загружало данные текущего аутентифицированного пользователя при старте. Без этой настройки после входа пользователя его данные будут недоступны - для работы с ними потребуется их ручная загрузка.

Следующий объект, который необходимо настроить - initOptions. Это объект конфигурации, позволяющий кастомизировать работу и инициализацию Keycloak. Устанавливаемые атрибуты включают:

1. onLoad - Принимает два значения:

  • login-required - принудительная аутентификация при запуске приложения:

    • Если пользователь авторизован в Keycloak - получает доступ к приложению;
    • Если не авторизован - отображается страница входа.
  • check-sso - позволяет вручную управлять статусом аутентификации и перенаправлениями.

2. checkLoginIframe - Определяет, должен ли Keycloak проверять статус авторизации через iframe. Требует осторожного использования - некорректная настройка может вызывать постоянные перезагрузки страницы.

3. redirectUri - URI (Uniform Resource Identifier) для перенаправления пользователя после успешного входа.

4. silentCheckSsoRedirectUri - Позволяет проверять статус SSO в фоновом режиме:

  • Без прямого перенаправления на сервер Keycloak и обратно;
  • Проверка выполняется в скрытом iframe.

Значение window.location.origin + /assets/silent-check-sso.html указывает на расположение статического HTML-файла, используемого для проверки Silent SSO. Этот файл обеспечивает взаимодействие между приложением и Keycloak через iframe.

Для реализации:

  1. 1. Создайте данный файл по указанному пути;
  2. 2. Добавьте в него следующий код:
	
<html>
  <body>
    <script>
      parent.postMessage(location.href, location.origin);
    </script>
  </body>
</html>
	

Доступные варианты могут отличаться в зависимости от версии Keycloak и особенностей приложения. Для получения актуальной информации обратитесь к официальной документации Keycloak.

Настройка сервиса аутентификации

После корректной инициализации и настройки модуля для работы с Keycloak вы сможете использовать предоставляемые им функции. Для этого создайте отдельный сервис authorization.service.ts, который будет содержать необходимые методы для работы с Keycloak в приложении.

Поскольку требуется обеспечить единственный экземпляр этого сервиса в приложении (singleton), используйте стандартный подход при создании сервисов через Angular CLI, оставив декоратор @Injectable({ providedIn: 'root' }). Затем в конструкторе внедрите сервис из библиотеки keycloak-angular:

  
constructor(private readonly keycloakService: KeycloakService) {}
  

После подготовки сервиса перейдем к реализации нескольких полезных методов. Первым будет метод для входа пользователей в приложение. Создайте метод redirectToLoginPage():

  
redirectToLoginPage(): Promise<void> {
    this.keycloakService.login();
}
  

Теперь перейдем к реализации оставшихся методов. Для данного проекта потребуется:

  • Получение информации о имени пользователя;
  • Проверка статуса авторизации;
  • Реализация выхода из системы.
  
get userName(): string {
  return this.keycloakService.getUsername();
}

isLoggedIn(): boolean {
  return this.keycloakService.isLoggedIn();
}

logout(): void {
  this.keycloakService.logout(environment.keycloak.postLogoutRedirectUri);
}
  

Как видите, API, предоставляемое keycloak-angular, позволяет легко реализовать все эти требования через вызов соответствующих методов на стороне Keycloak. Особое внимание стоит уделить методу logout(), в который можно передать параметр, определяющий путь для перенаправления пользователя после выхода из приложения. Как и ранее, этот параметр задаётся через переменные окружения.

Для реализации автоматического выхода пользователя после периода неактивности можно вызвать метод logout(), используя внешнюю библиотеку для отслеживания активности. Продемонстрируем это на примере библиотеки angular-user-idle.

После настройки (согласно официальной документации модуля) добавьте следующий код в файл app.component.ts:

  
ngOnInit(): void {
  if (this.authenticationService.isLoggedIn()) {
    this.userIdleService.startWatching();
    this.userIdleService
      .onTimerStart()
      .pipe(takeUntilDestroyed(this.destroyRef))
      .subscribe();
    this.userIdleService
      .onTimeout()
      .pipe(takeUntilDestroyed(this.destroyRef))
      .subscribe(() => {
        alert('Your session has timed out. Please log in again.');
        this.authenticationService.logout();
        this.userIdleService.resetTimer();
      });
  }
}
  

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

  • Автоматически выходить из приложения по истечении заданного времени бездействия;
  • Отображать соответствующее уведомление о выходе.

Важное замечание: Таймер необходимо сбрасывать при каждом вызове этой функции. В противном случае уведомление будет появляться постоянно, блокируя возможность выхода пользователя.

Маршрутизация приложения (Application Routing)

После интеграции с сервером Keycloak следующим шагом будет настройка маршрутизации приложения и реализация контроля доступа к отдельным страницам. Для этого проанализируем файл app.routes.ts, где:

  • Перечислены все маршруты приложения;
  • Для каждого роута настроен контроль доступа через guard canActivate.
  
export enum AppRoutes {
  Main = '',
  Protected = 'protected',
  Unprotected = 'unprotected',
  Logout = 'logout',
  NotFound = '404',
}

export const routes: Routes = [
  {
    path: AppRoutes.Main,
    pathMatch: 'full',
    component: MainPageComponent,
  },
  {
    path: AppRoutes.Protected,
    canActivate: [AuthGuard],
    component: ProtectedRouteComponent,
  },
  {
    path: AppRoutes.Unprotected,
    component: UnprotectedRouteComponent,
  },
  {
    path: AppRoutes.Logout,
    canActivate: [LogoutRouteGuard],
    component: LogoutScreenComponent,
  },
  {
    path: AppRoutes.NotFound,
    component: NotFoundComponent,
  },
  {
    path: '**',
    redirectTo: AppRoutes.NotFound,
  },
];
  

Все маршруты нашего приложения представлены выше. При анализе кода обратите внимание на ключевые элементы, отвечающие за логику авторизации.

Для ограничения доступа к определённым страницам используйте guards (защитники маршрутов) — они предотвратят доступ неавторизованных пользователей.

Пример такой страницы — Protected Page, защищённая AuthGuard. Давайте разберём принцип работы этого guard'а:

  
export const AuthGuard: CanActivateFn = (): boolean => {
  const authenticationService = inject(AuthenticationService);
  if (authenticationService.isLoggedIn()) {
    return true;
  }
  authenticationService.redirectToLoginPage();
  return false;
};
  

Принцип работы guard'а:

  1. При попытке доступа к защищённой странице guard проверяет авторизацию пользователя через метод isLoggedIn()

  2. Метод определяет наличие активной сессии:

    • Если сессия активна ? разрешает доступ;
    • Если сессии нет ? перенаправляет на страницу входа.

Особенности реализации:

  • Для страницы, появляющейся после выхода (logout), также можно применить guard;
  • Это ограничит доступ для пользователей с активной сессией Keycloak;
  • Проверка осуществляется тем же методом isLoggedIn() из authorizationService.
  
export const LogoutRouteGuard: CanActivateFn = () => {
  const authenticationService = inject(AuthenticationService);
  const router = inject(Router);
  if (!authenticationService.isLoggedIn()) {
    return true;
  } else {
    return router.createUrlTree([AppRoutes.Main]);
  }
};
  

Если Вы все сделали как опсиано в статье:

  • Откройте главную страницу приложения без авторизации
  • Запустите приложение командой:
  
ng serve
  

Возможно, вам будет интересно

Angular тестирование component с помощью Jest

Тестирование компонентов в Angular формально является задачей тестирования двух сущностей: Html шаблона и Typescript класса. И адекватное тестирование компонента заключается проверке корректной работы шаблона и класса.

Инструкция по деплою Angular приложения на Github Pages

В данной инструкции мы разберём, как можно задеплоить Angular приложение на Github Pages на примере нового приложения.

Angular Signals

Версия Angular 16 была выпущена в начале мая, и эта версия полна новых функций. Но одной из особенностей, о которой все говорят, является новая возможность - Сигналы. Это настоящий поворотный момент, который находится в стадии предварительного просмотра разработчиками и будет выпущен позже в этом году, в версии 17.

Формы. Реактивный подход.

Работа с формами. Реактивный подход (Reactive).

Оформление заявки

Документы на создание сайта

Изучите наше коммерческое предложение, заполните БРИФ и отправьте его на почту maxidebox@list.ru. Изучив все пожелания из БРИФ-а, обратным ответом оповестим Вас по стоимости разработке, ответим на вопросы.

КП на создание сайта Коммерческое предложение на созданеи сайта

Мы берем на себя ответственность за все стадии работы и полностью избавляем клиентов от забот и необходимости вникать в тонкости.

Скачать БРИФ (акета) на создание сайта Скачать БРИФ (акета) на создание сайта

Зополните у БРИФ-а все необходимые поля. Сделайте краткое описание к каждому из пунктов анкеты, привидите примеры в соответсвующий пунктах - это позволит лучше понять Ваши ожидания и требования к сайту