我已经创建了一个自定义Keycloak提供程序,该提供程序可以监听Kafka主题,并创建与该主题上收到的消息相对应的组。
我基本上可以正常工作了。我仍然需要克服的唯一障碍是,在创建组后,在管理UI或API调用中都未列出该组,以获取该领域的组列表。但是我确实知道这些组已保存在数据库中,因为如果我停止并重新启动Keycloak,那么它们就会存在。
我猜想这与Infinispan缓存未更新有关。我需要以某种方式告诉缓存刷新自身。有人知道该怎么做吗?
另一件事有些奇怪,我必须为此流程创建自己的KeycloakSession,并创建一个TransactionManager来管理事务。这是一种非常独特的情况,因为在Keycloak中触发的动作与用户会话没有关联;他们被赶走了Kafka消息。不确定是否与此有关。
我的设置是这样的:
GroupSyncApi
的自定义Spi实现。GroupSyncProvider
和GroupSyncProviderFactory
的接口Provider
和ProviderFactory
GroupSyncProvider
有一种方法:void listen(KeycloakSessionFactory keycloakSessionFactory)
KafkaGroupSyncProvider
和KafkaGroupSyncProviderFactory
来实现我的自定义界面。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);
}
我不清楚的部分是:
答案 0 :(得分:0)
我找到了解决方案。问题在于我如何将我的小组分配到其父小组。我是这样做的:
group.setParent(entitiesParent);
但是,仔细研究Keycloak源代码(我查看了GroupsResource
中创建组的admin API端点),发现Keycloak确实可以:
realm.moveGroup(group, entitiesParent);
moveGroup
方法进行了一堆缓存管理,包括使新组及其父级无效。这解决了我的问题;现在,通过我的提供商创建我的所有组后(在重新加载页面之后),它们都将在管理控制台中显示。这些组也可以通过API调用获得,以获取该领域的组列表。