1

我创建了一个自定义 Keycloak 提供程序,它侦听 Kafka 主题并创建与在该主题上收到的消息相对应的组。

我已经让它大部分工作了。我仍然需要克服的唯一障碍是,在创建组后,我看不到它在管理 UI 中或在 API 调用中列出以获取领域的组列表。但我确实知道这些组被保存在数据库中,因为如果我停止并重新启动 Keycloak,那么它们就会存在。

我猜这与未更新的 Infinispan 缓存有关。我需要以某种方式告诉缓存刷新自己。有人知道怎么做这个吗?

另一件有点奇怪的事情是,我必须为此流创建自己的 KeycloakSession,并连同它一起创建一个 TransactionManager 来管理事务。这是一种相当独特的情况,因为 Keycloak 中触发的操作与用户会话无关;他们被卡夫卡消息赶走了。不确定这是否与它有关。

我的设置是这样的:

  • 我创建了一个名为GroupSyncApi.
  • 我定义了接口GroupSyncProvider并分别GroupSyncProviderFactory扩展ProviderProviderFactory
  • GroupSyncProvider一种方法:void listen(KeycloakSessionFactory keycloakSessionFactory)
  • 我创建了实现KafkaGroupSyncProviderKafkaGroupSyncProviderFactory实现了我的自定义接口。
  • listen实现实例化了一个名为KafkaTopicListener. 这将启动 Kafka Consumer 和相关代码以在新线程中处理消息。该listen方法是从postInit提供者工厂的方法中调用的。这将启动线程和 Kafka 消费者。
  • 当收到一条消息(作为 JSON)时,我将它反序列化并创建一个与之对应的 Keycloak 组。我这样做如下(这是在我的KafkaTopicListener课上:

    public void listen(KeycloakSessionFactory keycloakSessionFactory) {
        while (true) {
            final ConsumerRecords<String, EntityEvent> records = consumer.poll(Duration.of(5, ChronoUnit.SECONDS));
            if (!records.isEmpty()) {
                KeycloakSession keycloakSession = keycloakSessionFactory.create();
                keycloakSession.getTransactionManager().begin();
                GroupModel entitiesParent = keycloakSession.realms().getTopLevelGroups(realm).stream().filter(groupModel -> "entities".equals(groupModel.getName())).findFirst().get();
    
                records.forEach(record -> {
                    logger.info("Received event on Kafka '" + record.topic() + "' topic.  " + record.value().getType() + " corresponding entity group with id: " + record.value().getEntity().getId() + "; name: " + record.value().getEntity().getName());
                    switch (record.value().getType()) {
                        case CREATE:
                            doCreate(keycloakSession, realm, record.value(), parent);
                            break;
                    }
                }
                keycloakSession.getTransactionManager().commit();
                consumer.commitAsync();
            }
        }
    }
    
    private void doCreate(KeycloakSession keycloakSession, RealmModel realm, EntityEvent event, GroupModel entitiesParent) {
        GroupModel group = keycloakSession.realms().createGroup(realm, event.getEntity().getName());
        group.setAttribute("type", singleValue("entity"));
        group.setParent(entitiesParent);
    }
    

我不清楚的部分是:

  • 我是否正确创建了一个组?
  • 我的 KeycloakSession 设置是否正确?我必须手动创建它,因为此代码流不是 HTTP 请求的一部分。
  • 我的事务管理器设置正确吗?
4

1 回答 1

0

我找到了解决方案。问题在于我如何将我的组分配给它的父组。我这样做了:

group.setParent(entitiesParent);

但是通过挖掘 Keycloak 源代码(我查看了GroupsResource创建组的管理 API 端点),我看到 Keycloak 改为:

realm.moveGroup(group, entitiesParent);

moveGroup方法执行一系列缓存管理,包括使新组及其父组失效。这解决了我的问题;我现在在管理控制台中看到我的所有组(在页面重新加载后),它们是通过我的提供程序创建的。这些组也可以通过 API 调用来获取领域的组列表。

于 2019-07-08T21:19:59.207 回答