Рубрики
Без рубрики

Защитите свое приложение Java Servlet с помощью Keycloak

Мы увидим, как настроить приложение на основе Java-сервлетов, чтобы оно было защищено с помощью Keycloak. Кейк… Помеченный java, keycloak.

Мы увидим, как настроить приложение на основе 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”