在此博客中,我将讨论如何为KeyCloak构建事件侦听器插件(称为SPI)。
那么,什么是Keycloak ?
Keycloak是RedHat构建的开源身份和访问管理框架。 它提供了许多高级功能,例如SSO,社交身份验证,对多种身份验证协议的支持等。在此处了解更多信息: https : //www.keycloak.org/
但是最重要的功能之一是能够通过简单地构建插件来扩展KeyCloak的任何功能。
在今年夏天的实习期间,我们需要记录KeyCloak中发生的所有用户(和管理员)事件,并将它们发送到外部系统进行分析。 在许多情况下都需要这样做。 (一个示例是,如果您使用外部SIEM记录和分析事件) 。
默认情况下,KeyCloak日志不包含用户/管理员事件。 即使启用了该功能,也很难构建一个外部系统来监视和解析日志以提取所需的事件。 相反,我们可以为KeyCloak构建一个插件以挂接到系统中,并在事件发生时执行“某些操作” (在我们的情况下,触发外部API调用)
因此,让我们建立一个:)
注意 :这里提供了事件侦听器的完整代码。
adwait-thattey / keycloak-event-listener-spi
示例事件侦听器SPI,用于keycloak
我将在这里使用Maven来管理依赖项和构建项目。
因此,让我们首先整理一下pom.xml 。
(如果您不熟悉Maven,我们将在Maven中使用pom.xml文件列出所有项目详细信息,包括所有依赖项)
(如果看不到上述要点,则可以在此处找到该文件
在pom.xml
,我们定义了父级详细信息,项目名称Sample Event Listener
,版本,工件ID(此处为sample_event_listener
),依赖项和构建配置。
下一步是实施SPI。 为此,我们需要实现2个类。 Provider
和ProviderFactory
因此,让我们在src/main/java
创建我们的包。
此处的包名称为com.coderdude.sampleeventlistenerprovider.provider
coderdude
:因为我的开发别名是coderdude:D
sampleeventlistenerprovider
:可以更短一些,但让我们保留
provider
:最后一个提供程序在那里,因为您的提供程序中可能会使用其他模块。
现在,此软件包将包含上面讨论的2个类。
Provider
类包含插件的实际逻辑。 ProviderFactory
是用于初始化ProviderFactory
的包装器。 区别很重要 。
- 仅在启动KeyCloak时初始化
Factory
。 每次需要时,Factory
都会创建一个Provider
的新实例。 (就我们而言,每次事件发生) - 仅存在1个
Factory
实例。 多个提供程序可以同时存在(例如2个事件同时发生)。 - 提供程序完成任务后将被销毁。 只要KeyCloak运行,工厂就存在。
- Factory中的任何错误都将使KeyCloak崩溃。 Provider中的错误只会进入日志,而Keycloak的其余部分将正常运行
因此,让我们从创建提供者开始。
的类的名称将是SampleEventListenerProvider
它实现了EventListenerProvider
接口(该接口由KeyCloak提供)
package com . coderdude . sampleeventlistenerprovider . provider ;
import org.keycloak.events.Event ;
import org.keycloak.events.EventListenerProvider ;
import org.keycloak.events.admin.AdminEvent ;
import java.util.Map ;
public class SampleEventListenerProvider implements EventListenerProvider {
public SampleEventListenerProvider () {
}
}
暂时保留这些进口。 我们将需要它们。
因此,在这里,我们将把所有事件打印到控制台。 所有事件均由2个类提供: org.keycloak.events.Event
和org.keycloak.events.admin.AdminEvent
每当普通用户执行某项操作时,就会发生普通事件。 管理员执行某事时会发生管理事件。
我们需要编写适当的方法将这些类对象转换为可读的字符串。
这是为Event
构建字符串的方法
我们正在捕获所有参数,错误和详细信息。 (因此映射,因为细节是一个数组)
private String toString ( Event event ) {
StringBuilder sb = new StringBuilder ();
sb . append ( "type=" );
sb . append ( event . getType ());
sb . append ( ", realmId=" );
sb . append ( event . getRealmId ());
sb . append ( ", clientId=" );
sb . append ( event . getClientId ());
sb . append ( ", userId=" );
sb . append ( event . getUserId ());
sb . append ( ", ipAddress=" );
sb . append ( event . getIpAddress ());
if ( event . getError () != null ) {
sb . append ( ", error=" );
sb . append ( event . getError ());
}
if ( event . getDetails () != null ) {
for ( Map . Entry < String , String > e : event . getDetails (). entrySet ()) {
sb . append ( ", " );
sb . append ( e . getKey ());
if ( e . getValue () == null || e . getValue (). indexOf ( ' ' ) == - 1 ) {
sb . append ( "=" );
sb . append ( e . getValue ());
} else {
sb . append ( "='" );
sb . append ( e . getValue ());
sb . append ( "'" );
}
}
}
return sb . toString ();
}
当然,这是一个非常幼稚的实现。 我们实际上所做的是定义将这些事件包装在其他对象中并向外部系统进行API调用的方法。 但这现在将起作用。
我们可以为AdminEvent
构建类似的方法。 您将在主要的完整代码中找到它。
完成此操作后,我们需要覆盖由
EventListenerProvider
接口。 这些是onEvent
和close
。
这里是
@Override
public void onEvent ( Event event ) {
System . out . println ( "Event Occurred:" + toString ( event ));
}
@Override
public void onEvent ( AdminEvent adminEvent , boolean b ) {
System . out . println ( "Admin Event Occurred:" + toString ( adminEvent ));
}
@Override
public void close () {
}
onEvent
是事件发生时实际调用的方法。 我们需要两次重载onEvent来捕获Event
和AdminEvent
。
最后,在销毁类之前立即调用close
方法。 有点像一个破坏者。 即使我们不需要使用它,也需要重写它。
您可以在此处找到完整的类代码(以及AdminEvent的字符串实现)
下一步是实现ProviderFactory
类的名称是SampleEventListenerProviderFactory
它实现EventListenerProviderFactory
这是代码:
(如果看不到上述要点,则可以在此处找到该文件
我们在这里覆盖了多种方法。 主要的是create
和getId
。 create方法应该初始化并返回提供者的实例(在我们的示例中为SampleEventListenerProvider
)。 getId
应该返回一个带有插件名称的字符串
下一个也是最后一个任务是提供指向我们班级的链接。 为此,我们需要创建资源。
在src/main
创建一个名为resources
的文件夹(与java
文件夹一起)
现在,在resources/META-INF/services/
创建以下文件,名为org.keycloak.events.EventListenerProviderFactory
。 请注意,文件位置的完整路径是src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory
该文件仅包含一行内容,其中包含我们工厂类的软件包和名称
com . coderdude . sampleeventlistenerprovider . provider . SampleEventListenerProviderFactory
而已。 我们已经编写了插件。 现在,我们来构建和打包它。
我用过Maven来构建和打包
打包完成后,您应该在目标目录中看到jar
和sources
这是我的最终目录结构
我们只需要sample-event-listener.jar
-
现在是时候将插件部署到KeyCloak了。
让我们首先使用KeyCloak进行设置。 您可以在https://www.keycloak.org/docs/latest/getting_started/index.html上找到入门指南。
快速下载并创建管理员用户,然后登录KeyCloak。
现在,我们创建一个名为newrealm
的新领域,并在该新领域中添加一个名为newuser001
的用户。
我们还为该新用户创建一个密码
是时候部署我们很棒的插件了
部署过程非常简单。 我们需要将sample-event-listener.jar
复制到$KEYCLOAK_DIR/standalone/deployments/
,其中$ KEYCLOAK_DIR是主KeyCloak目录(解压缩后)
KeyCloak支持热重装。 因此,一旦我们复制了jar文件,keycloak应该重新加载并部署插件。 但是请确保我们重新启动Keycloak服务器。
您应该会看到这样的一行
Deployed "sample-event-listener.jar" (runtime-name : "sample-event-listener.jar")
现在,我们需要允许该插件监听事件。
转到newrealm->manage->events->config
或此URL /auth/admin/master/console/#/realms/newrealm/events-settings
确保将newrealm替换为您创建的领域的名称
在事件监听器配置中,将sample_event_listener
添加到列表中,然后点击保存。
现在,我们的插件应该能够捕获所有事件。
让我们测试一下
使用上面创建的用户登录到新领域。
您应该看到控制台中发生了一个事件
17:03:01,797 INFO [stdout] (default task-5) Event Occurred:type=LOGIN, realmId=newrealm, clientId=account, userId=efc09972-6166-4ed6-9ca0-15c030e47f54, ipAddress=127.0.0.1, auth_method=openid-connect, auth_type=code, redirect_uri=http://localhost:8180/auth/realms/newrealm/account/login-redirect, consent=no_consent_required, code_id=78db58ed-3c99-4d42-aced-b69873c59f12, username=newuser001
注销也应被捕获
17:03:51,211 INFO [stdout] (default task-5) Event Occurred:type=LOGOUT, realmId=newrealm, clientId=null, userId=efc09972-6166-4ed6-9ca0-15c030e47f54, ipAddress=127.0.0.1, redirect_uri=http://localhost:8180/auth/realms/newrealm/account/
还会尝试使用错误的密码登录(因为我们也在捕获错误)
17:04:04,505 WARN [org.keycloak.events] (default task-5) type=LOGIN_ERROR, realmId=master, clientId=security-admin-console, userId=null, ipAddress=127.0.0.1, error=user_not_found, auth_method=openid-connect, auth_type=code, redirect_uri=http://localhost:8180/auth/admin/master/console/#/realms/newrealm/users, code_id=59a85ee0-a8f6-4fad-8667-f72de2da18fd, username=newuser001
在我的控制台中看起来像这样
瞧! 我们的插件能够捕获事件
包起来:
再一次,完整的代码在这里可用:
adwait-thattey / keycloak-event-listener-spi
示例事件侦听器SPI,用于keycloak
这是一个非常基本的例子。 我们可以做的更多。 keycloak事件提供了很多有用的信息,这些信息可以捕获。 像当前领域一样,尝试登录的人的IP地址,如果是api登录,则访问令牌ID,等等。
如果您喜欢此博客,请点击:)
再见!
等待Thattey,
https://adwait-thattey.github.io/
From: https://dev.to/adwaitthattey/building-an-event-listener-spi-plugin-for-keycloak-2044