Реализация SPI для пользовательского хранилища Keycloak

Я пытаюсь реализовать настраиваемый SPI аутентификатора keycloack для аутентификации по внешнему источнику данных. Также доступна служба Spring boot Rest, я тоже могу ее использовать.

Пример использования, который я пытаюсь решить,

Пользователю предоставляется экран входа в Keycloak. Пользователь с отправкой проверяется по внешнему источнику данных.

Получите некоторые атрибуты из внешнего источника данных, сопоставьте их с идентификатором keycloak и токеном доступа.

Также поставьте условие ограничения пользователей на одновременный вход одного и того же пользователя в систему несколько раз.

Я думал, что это можно решить, получив информацию о сеансе пользователя, доступную в источнике данных keycloak. Если я использую внешний источник данных, сохраняет ли keycloak информацию о сеансе?

Я следил за разделом 8.3 официального руководства (https://www.keycloak.org/docs/latest/server_development/index.html#_auth_spi_walkthrough), что очень похоже на то, что мне нужно.

Теперь я пропустил и начал согласно разделу 11 (https://www.keycloak.org/docs/latest/server_development/index.html#_user-storage-spi) также кажется более подходящим.

То, что я сделал, началось с реализации пользовательского аутентификатора SPI, который решил, что это неправильный путь, и теперь реализовал UserStorageProvider.

/***
 * From UserLookupProvider
 */
public UserModel getUserById(String id, RealmModel realm) {
    System.out.println("ID: " + id + ":REALM:" + realm);
    StorageId storageId = new StorageId(id);
    /**
     * StorageId.getExternalId() method is invoked to obtain 
     * the username embeded in the id parameter
     */
    String username = storageId.getExternalId();
    System.out.println("Name:" + username);
    return getUserByUsername(username, realm);
}

/***
 * From UserLookupProvider
 * This method is invoked by the Keycloak login page when a user logs in
 */
public UserModel getUserByUsername(String username, RealmModel realm) {
    System.out.println("USERNAME: " + username + ":REALM:" + realm);
    UserModel userModel = loadedUsers.get(username);
    if (userModel == null) {
        String password = properties.getProperty(username);
        if (password != null) {
            userModel = createUserModel(realm, username);
            System.out.println("New UserModel:");
            System.out.println(userModel.toString());
            loadedUsers.put(username, userModel);
        }
    }
    return userModel;
}

protected UserModel createUserModel(RealmModel realm, String username) {
    return new AbstractUserAdapter(session, realm, model) {
        @Override
        public String getUsername() {
            return username;
        }
    };
}

Следуйте документу (https://www.keycloak.org/docs/latest/server_development/index.html#packaging-and-deployment-2)

Файлы классов для нашей реализации провайдера должны быть помещены в jar. Вы также должны объявить фабричный класс провайдера в файле META-INF / services / org.keycloak.storage.UserStorageProviderFactory.

Вопрос вот в чем: jar, который я создал, не имеет каталога служб внутри папки "META-INF", нужно ли мне создавать и добавлять его вручную?

org.keycloak.examples.federation.properties.FilePropertiesStorageFactory Создав банку, вы можете развернуть ее, используя обычные средства WildFly: скопируйте банку в каталог deploy / или с помощью интерфейса командной строки JBoss.

После создания jar-файла с помощью maven скопируйте jar-файл в папку «keycloak-6.0.1 \ standalone \ deployments». но я не вижу своего провайдера в «списке Федерации пользователей»  Список федерации пользователей

Любое предложение / помощь будет принята с благодарностью!

Заранее благодарим за ваши предложения.


person amj    schedule 13.08.2019    source источник


Ответы (3)


  • Хорошо, вы пояснили, что вам нужен API поставщика хранилища пользователей. Большой

  • Теперь что касается вашей второй «проблемы / вызова»:

Получить некоторые атрибуты из внешнего источника данных, сопоставить их с идентификатором keycloak и токеном доступа. Необходимо получить уникальный идентификатор пользователя и добавить его в качестве идентификатора субъекта в jwt. Это идентификатор, остальные службы могут использовать его для получения идентификатора, когда этот токен передается другим службам.

Лучшее, что вы можете сделать для этого:

  1. Добавьте уникальные данные этого пользователя в качестве атрибутов пользователей (см. Их в консоли администратора).

  2. Создайте «Клиентскую область» на Keycloak с картографом для «свойства пользователя», чтобы сопоставить те свойства, которые вы хотите добавить (от вашего пользователя) к вашему идентификатору-токену и токену доступа. Вам также необходимо связать вашего клиента с только что созданной «клиентской областью». Это может показаться немного запутанным, но это видео - отличный материал, и я верю, что оно вам очень поможет: https://www.youtube.com/watch?v=ZxpY_zZ52kU (примерно в 6:30 вы увидите, как добавить дополнительную информацию о пользователе к своим токенам)

также проверьте эту страницу: https://jwt.io/ (когда вы вставляете туда закодированные токены, вы можете увидеть их содержимое), это отличный инструмент для разработчиков.

Когда вы продвинетесь в своем решении, я помогу с уникальным сеансом, или вы опубликуете это как другой вопрос, так как это другая проблема.

Надеюсь, это поможет.

person tony _008    schedule 16.08.2019
comment
@ tony-008 Для уникального сеанса пользователя я разместил отдельный вопрос (stackoverflow.com/questions/57643410). Пожалуйста, дайте мне знать ваши мысли / предложения. - person amj; 25.08.2019
comment
Что бы вы думали о лимите сеанса пользователя Keycloak. Я сделал это с помощью Admin Rest api. получить токен администратора и использовать эту конечную точку (/ auth / admin / realms / {realmId} / users / {userId} / sessions) для получения сеанса пользователя и его проверки. Я чувствую, что должен быть способ получше - person amj; 05.09.2019
comment
@amj. Я не реализовал это, но если бы мне пришлось, мой подход был бы следующим: если вы перейдете в раздел административной области / сеансов, вы увидите количество открытых сеансов для каждого пользователя-клиента / пользователя. Должна быть возможность программно запрашивать их. В настраиваемом методе validate () вашего ProviderStore я бы добавил условие проверки, что у рассматриваемого пользователя должно быть 0 ранее существовавших сеансов и не более (только если вы используете пароль, если используете токен, то не потому, что вы хотите состоит в том, чтобы избежать создания токена, если сеанс пользователя уже существует). При выполнении этого токена перевыпуск должен происходить 1 к 1. - person tony _008; 06.09.2019

Если кто-то столкнулся с такими проблемами:

UserStorage SPI не отображался из-за папки META-INF / services. Это указано в документации, но не ясно

В src / main / resources создайте структуру папок META-INF / services

Создайте файл с именем org.keycloak.storage.UserStorageProviderFactory (все это имя файла) в каталоге META-INF / services. Его содержимое - это полное имя класса вашего SPI: com.test.UserSpi.

развернуть банку

person amj    schedule 14.08.2019
comment
Можете ли вы поделиться всей структурой каталогов для всех файлов. Также пытаюсь создать UserStorage SPI. - person Tannu; 18.09.2020

Я не совсем уверен, что тебе нужно. Начнем с различения SPI аутентификации (федеративной проверки идентичности) и SPI провайдера пользователя (федеративных пользователей). Первый (раздел 8 документа - больше касается аутентификации пользователей с помощью внешней службы - аналогично facebook или google). Объединенное хранилище пользователей больше похоже на то, что у вас есть собственные пользователи в унаследованной системе с их унаследованной «структурой ролей», и вы в основном хотите управлять ими с помощью keycloak (либо импортируя их, либо запрашивая через некоторый API - это будет раздел 11 этой документации). Поэтому, пожалуйста, решите, что именно вам нужно.

Во-вторых, вы упоминаете следующее:

>  User is presented keycloak login screen. Onsubmission User is
> validated against external Datasource.
> 
> Retrieve some attributes from external datasource, map it to
> keycloak's id and access token.
> 
> Also put in a condition of user restriction of same user logging in
> multiple times at the same time.
> 
> I was thinking, it could be solved by retrieving user session
> information that's available in the keycloak datasource. If i use
> external datasource, does keycloak still maintain session information?

Что вы имеете в виду под: Получить некоторые атрибуты из внешнего источника данных, сопоставить их с идентификатором keycloak и токеном доступа.? Обычно вы получаете только базовую информацию пользователя, а также, возможно, роли и другие настраиваемые атрибуты (но не информацию о сеансе). Сам Keycloak как сервер авторизации на основе openIDConnect будет генерировать токены доступа, которые уже содержат информацию о том, какой защищенный ресурс может получить доступ, поэтому вам действительно не нужно импортировать какой-либо сеанс из другого места или беспокоиться о генерации указанных токенов. .

в отношении: Также поставьте условие о том, что один и тот же пользователь будет входить в систему несколько раз одновременно.. Что именно вы пытаетесь достичь (или избежать?), когда вы войдите в первый раз, когда ваш клиент получит токен на предъявителя, действительный в течение X времени, в течение этого времени вам не нужно будет снова входить в систему, пока токен не истечет или не будет перемещен; снова то, о чем заботится ваш сервер Auth, а не то, что вы реализуете. Вы хотите что-то более конкретное?

Я подумал, что это можно решить, получив информацию о сеансе пользователя, доступную в источнике данных keycloak. Если я использую внешний источник данных, хранит ли keycloak информацию о сеансе? Звучит неправильно, к каким данным сеанса вы обращаетесь? или вам нужен доступ? к вашим пользовательским данным, областям действия, ролям и т. д. можно получить доступ через KEycloak Rest API (https://www.keycloak.org/docs-api/6.0/rest-api/index.html#_overview). Ваш внешний источник данных предназначен для основных данных, связанных с пользователем (а не для внешних сеансов). Как вы думаете, почему вам нужно импортировать внешний сеанс?

person tony _008    schedule 14.08.2019
comment
Спасибо @ tony_008 за ваши комментарии. Я собираюсь опубликовать несколько комментариев, чтобы ответить на ваш вопрос. Прошу прощения за это, это из-за ограничений персонажа - person amj; 16.08.2019
comment
Я начал с Custom Authenticator для аутентификации по внешнему источнику данных. Как вы отметили ранее, это не предназначено для этого. Теперь я начал внедрять User Provider SPI. Объединенный пользовательский магазин - это то, что мне нужно. Теперь работает, с некоторыми глюками. - person amj; 16.08.2019
comment
Получить некоторые атрибуты из внешнего источника данных, сопоставить их с идентификатором keycloak и токеном доступа. Необходимо получить уникальный идентификатор пользователя и добавить его в качестве идентификатора субъекта в jwt. Это идентификатор, остальные службы могут использовать его для получения идентификатора, когда этот токен передается другим службам. - person amj; 16.08.2019
comment
сопоставьте его с идентификатором keycloak и токеном доступа. Я имел в виду токен идентификатора подключения openId keycloak - person amj; 16.08.2019
comment
Также поставьте условие ограничения для пользователей входа одного и того же пользователя в систему. несколько раз одновременно. Что мне нужно здесь решить, так это то, что мне нужно предотвратить вход одного и того же пользователя в систему из нескольких систем в в то же время. Итак, я подумал, что лучший способ - проверить их на sigin с сохраненной информацией о сеансе. Если есть лучший способ проверки и предотвращения, просьба дать предложение / комментарий. - person amj; 16.08.2019
comment
Привет, у вас есть вопрос по этому поводу (например, уникальный вопрос без ограничений по месту)? - person tony _008; 05.09.2019