меню

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
  

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

10 лучших UI библиотек для Angular

Angular зарекомендовал себя, как один из самых популярных фреймворков JavaScript. Его компонентная архитектура позволяет разработчику разделить приложение на небольшие многократно используемые фрагменты.

Маршрутизация Angular подробное руководство

Маршрутизация Angular подробное руководство. Без маршрутизации (роутинга) вы никогда не сделаете качественное приложение.

Route. Обновление шаблона

Обновление шаблона при динамическом роутинге и отписка от наблюдения

Кастомные элементы форм в Angular

Реактивные формы — это одна из лучших вещей в Angular. Поскольку они упрощают работу с большими и действительно динамичными формами. В этой мы рассмотрим создание кастомного элемента формы.

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

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

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

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

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

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

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