在Keycloak中以编程方式创建组后使缓存无效

时间:2019-07-06 00:51:04

标签: keycloak

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

我基本上可以正常工作了。我仍然需要克服的唯一障碍是,在创建组后,在管理UI或API调用中都未列出该组,以获取该领域的组列表。但是我确实知道这些组已保存在数据库中,因为如果我停止并重新启动Keycloak,那么它们就会存在。

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

另一件事有些奇怪,我必须为此流程创建自己的KeycloakSession,并创建一个TransactionManager来管理事务。这是一种非常独特的情况,因为在Keycloak中触发的动作与用户会话没有关联;他们被赶走了Kafka消息。不确定是否与此有关。

我的设置是这样的:

  • 我创建了一个名为GroupSyncApi的自定义Spi实现。
  • 我定义了分别扩展GroupSyncProviderGroupSyncProviderFactory的接口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请求的一部分。
  • 我的交易管理器设置正确吗?

1 个答案:

答案 0 :(得分:0)

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

group.setParent(entitiesParent);

但是,仔细研究Keycloak源代码(我查看了GroupsResource中创建组的admin API端点),发现Keycloak确实可以:

realm.moveGroup(group, entitiesParent);

moveGroup方法进行了一堆缓存管理,包括使新组及其父级无效。这解决了我的问题;现在,通过我的提供商创建我的所有组后(在重新加载页面之后),它们都将在管理控制台中显示。这些组也可以通过API调用获得,以获取该领域的组列表。

相关问题