Мы увидим, как настроить приложение на основе Java-сервлетов, чтобы оно было защищено с помощью Keycloak.
Keycloak – это система управления идентификацией и доступом с открытым исходным кодом, которая может использоваться для полного делегирования безопасности приложения.
1. Конфигурация скрытого ключа
Документация Keycloak действительно проста в использовании. Вы можете сами ознакомиться здесь с разделом о конфигурации вашего экземпляра Keycloak: https://www.keycloak.org/docs/latest/authorization_services/#_getting_started_hello_world_create_realm
Вам необходимо настроить:
- Царство
- Пользователь с ролью
user
, позже мы увидим, как он используется - Клиент. Это представление вашего Java-приложения
- Клиентский протокол: openid-connect
- Тип доступа: общедоступный
- Допустимые URI перенаправления: URL-адрес вашей среды разработки или
*
на время существование
2. Ограничение безопасности Tomcat
Мы используем Tomcat security-constraint
, который обеспечивает проверку безопасности на уровне приложения в Tomcat. Команда Keycloak разработала удобный клапан для системы безопасности Tomcat, который обрабатывает перенаправление на страницу входа Keycloak и обратно.
2.1. Вам необходимо добавить следующее в context.xml вашего заявления:
2.2. Установите библиотеки Keycloak Valve в каталог ${tomcat}/lib на вашем сервере Tomcat
2.3. Вам необходимо скопировать конфигурационный файл keycloak.json в/WEB-INF/keycloak.json
Вы можете загрузить файл на вкладке установки вашего клиента:
2.4. Добавьте ограничение безопасности в свой web.xml
Private area /esp_privat/* user Public area /api/* BASIC this is ignored currently user
Здесь мы определили 2 шаблона URL-адресов:
/esp_privat/esp_privat/*
которые требуют, чтобы пользователь был связан с рольюпользователь
/api/*
которые не требуют аутентификации
2.5. Результаты
Поэтому, когда вы пытаетесь получить доступ к любому маршруту в разделе /esp_privat/esp_privat/
в вашем приложении Keycloak valve теперь автоматически перенаправляет вас на страницу входа в ваш экземпляр Keycloak. При успешном входе в систему Keycloak перенаправляет вас на запрошенную страницу.
Что нам нужно сделать сейчас, так это идентифицировать пользователя, вошедшего в систему благодаря токену Keycloak, который добавляется в файлы cookie веб-навигатора.
3. Перехватите токен доступа Keycloak для входа пользователя в ваше приложение
3.1. Зависимости от ключевого ключа
Добавьте следующее в pom.xml
вашего веб-приложения
приложения:
org.keycloak keycloak-core 9.0.2 provided org.keycloak keycloak-adapter-core 9.0.2 provided org.keycloak keycloak-adapter-spi 9.0.2 provided
Обратите внимание на область видимости = предоставлено
, так как мы будем использовать библиотеки, добавленные ранее в папку библиотеки tomcat. Мы не хотим переопределять его другой версией библиотек.
3.2. Прочтите жетон
Следующий фрагмент извлечет токен из запроса и проверит, истек ли срок его службы. Он возвращает значение true в случае, если токен действителен.
import org.keycloak.KeycloakSecurityContext; import org.keycloak.TokenVerifier; import org.keycloak.common.VerificationException; import org.keycloak.representations.AccessToken; ... /** * Verify if user is logged in keycloak by validating token in request */ public boolean isLoggedInKeycloak(HttpServletRequest request) throws VerificationException { KeycloakSecurityContext keycloakSecurityContextToken = getKeycloakSecurityContextToken(request); if (keycloakSecurityContextToken == null) { return false; } return !isTokenExpired(keycloakSecurityContextToken); } private boolean isTokenExpired(KeycloakSecurityContext keycloakSecurityContextToken) throws VerificationException { AccessToken token = TokenVerifier.create(keycloakSecurityContextToken.getTokenString(), AccessToken.class).getToken(); if (token.isExpired()) { logger.warn("User token is expired..." + token); return true; } return false; }
В нашем случае нам также нужно было проверить, является ли пользователь членом правильной группы, поэтому мы добавили следующий метод проверки:
private void handleGroupMembership(@Nonnull KeycloakSecurityContext keycloakSecurityContext, String keycloakPreferredUsername) { Object groups = keycloakSecurityContext.getToken().getOtherClaims().getOrDefault("groups", new ArrayList<>()); if (groups == null) { throw new GenericRuntimeException("Fail to read groups from the token of the user " + keycloakPreferredUsername); } ((List) groups) .stream() .filter(s -> s.equalsIgnoreCase("/my-group")) .findFirst() .orElseThrow(() -> new GenericRuntimeException("User \"" + keycloakPreferredUsername + "\" is not a member of /my-group")); }
Затем мы вызвали предыдущий метод в предварительном действии для всех вызовов, полученных нашими сервлетами, чтобы он мог быть перехвачен любым сервлетом следующим образом:
boolean isUserLoggedIn = request.getSession().getAttribute(USER_SESSION) != null; if (isLoggedInKeycloak(request) && !isUserLoggedIn) { logger.info("User logged in Keycloak but not logged in the app. Logging in the user..."); new KeycloakLoginService().login(request, getKeycloakSecurityContextToken(request)); } else if (!isLoggedInKeycloak(request) && isUserLoggedIn) { logger.info("User not logged in Keycloak but logged in the app. Logging out the user..."); sessionLogout.logout(request, response); return; }
3.3. Выход из системы
Для выхода пользователя из Keycloak вы можете использовать метод request.logout()
. Мы используем следующий метод:
public void logout(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(false); if (session != null) { session.invalidate(); } request.logout(); request.getSession(true); // create a new session response.sendRedirect(request.getContextPath()); }
4. Профили Maven для компиляции версий с логином keycloak и без него
В одном из наших проектов нам нужно было иметь возможность развернуть версию приложения, которая не использует функцию входа в систему Keycloak, но наш предыдущий механизм входа в систему. Конечно, мы хотели сохранить уникальную кодовую базу с как можно меньшим различием. Мы определяем, что единственное, что мешало нам работать по-прежнему, – это раздел security-constraint
в в web.xml
конфигурационный файл.
Мы будем использовать решение для фильтрации Maven с небольшим взломом, который мы нашли на SO: https://stackoverflow.com/questions/3298763/maven-customize-web-xml-of-web-app-project/8593041#8593041 Он состоит в добавлении 2 переменных в ваш web.xml вот так:
${enable.security.start}... // all of the XML that you need, in a completely readable format ... ${enable.security.end}
И замените его на начало блока комментариев --
и заканчивайте ->
в профиле, где вы не хотите использовать Keycloak.
Так что в нашем дефолте ci
профиль мы определили следующие свойства:
и в профиле without-keycloak
:
Оригинал: “https://dev.to/m4nu56/secure-your-java-servlet-application-with-keycloak-4826”