如何订阅Keycloak事件?请求提供Java处理user_temporarily_disabled示例
Keycloak实时接收user_temporarily_disabled事件的Java实现方案
一、可行性说明
完全可行,Keycloak原生支持实时事件捕获机制,无需定时轮询。当用户因连续输错密码被临时禁用时,会触发USER_TEMPORARILY_DISABLED事件,我们可以通过自定义事件监听器或Webhook两种方式实时接收并处理。
二、核心实现方案:自定义Keycloak事件监听器
通过开发自定义事件监听器Jar包部署到Keycloak,事件触发时直接调用本地业务服务接口,是最直接的Java实现方式。
1. Maven依赖配置
注意版本需与你的Keycloak版本匹配,示例使用22.0.5版本:
<dependencies> <dependency> <groupId>org.keycloak</groupId> <artifactId>keycloak-core</artifactId> <version>22.0.5</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.keycloak</groupId> <artifactId>keycloak-server-spi</artifactId> <version>22.0.5</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.keycloak</groupId> <artifactId>keycloak-server-spi-private</artifactId> <version>22.0.5</version> <scope>provided</scope> </dependency> <!-- 调用外部服务所需的HTTP客户端 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>3.1.2</version> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> </dependencies>
2. 自定义事件监听器代码
事件处理核心类
import org.keycloak.events.Event; import org.keycloak.events.EventListenerProvider; import org.keycloak.events.EventType; import org.keycloak.events.admin.AdminEvent; import org.keycloak.models.KeycloakSession; import org.springframework.web.client.RestTemplate; public class UserDisabledEventListener implements EventListenerProvider { private final KeycloakSession session; private final RestTemplate restTemplate; // 替换为你的本地业务服务地址 private static final String BUSINESS_SERVICE_URL = "http://your-local-service/api/user/disabled-handler"; public UserDisabledEventListener(KeycloakSession session) { this.session = session; this.restTemplate = new RestTemplate(); } @Override public void onEvent(Event event) { // 仅处理用户临时禁用事件 if (EventType.USER_TEMPORARILY_DISABLED.equals(event.getType())) { String userId = event.getUserId(); String username = session.users().getUserById(session.getContext().getRealm(), userId).getUsername(); // 构造业务请求参数 UserDisabledRequest request = new UserDisabledRequest(); request.setUserId(userId); request.setUsername(username); request.setDisableReason("连续10次输入错误密码"); // 调用本地业务服务 try { restTemplate.postForObject(BUSINESS_SERVICE_URL, request, Void.class); } catch (Exception e) { // 异常日志记录 session.getContext().getLogger().error("处理用户禁用事件时调用业务服务失败", e); } } } @Override public void onEvent(AdminEvent adminEvent, boolean includeRepresentation) { // 无需处理管理端事件可留空 } @Override public void close() { // 资源释放操作 } // 请求参数内部类 private static class UserDisabledRequest { private String userId; private String username; private String disableReason; // Getter & Setter public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getDisableReason() { return disableReason; } public void setDisableReason(String disableReason) { this.disableReason = disableReason; } } }
监听器工厂类
import org.keycloak.Config; import org.keycloak.events.EventListenerProvider; import org.keycloak.events.EventListenerProviderFactory; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; public class UserDisabledEventListenerFactory implements EventListenerProviderFactory { @Override public EventListenerProvider create(KeycloakSession session) { return new UserDisabledEventListener(session); } @Override public void init(Config.Scope config) { // 可从Keycloak配置文件读取自定义参数,如业务服务地址 } @Override public void postInit(KeycloakSessionFactory factory) { // 后续初始化操作 } @Override public void close() { // 资源释放 } @Override public String getId() { // 监听器唯一标识,控制台配置时需使用此值 return "user-disabled-event-listener"; } }
3. 打包与部署
- 在
src/main/resources/META-INF/services目录下创建文件org.keycloak.events.EventListenerProviderFactory,内容为工厂类的全限定名:com.yourcompany.keycloak.listener.UserDisabledEventListenerFactory - 用Maven打包成Jar包,放入Keycloak的
providers目录 - 重启Keycloak,在对应Realm的Events > Event Listeners中添加
user-disabled-event-listener
三、备选方案:Webhook事件订阅(无需自定义Jar)
若不想开发自定义监听器,可直接通过Keycloak控制台配置Webhook:
- 进入对应Realm的Events > Settings,开启事件记录并勾选
USER_TEMPORARILY_DISABLED - 进入Events > Webhooks,添加Webhook,设置本地服务接口地址,选择触发事件为
USER_TEMPORARILY_DISABLED - 注意:需确保Keycloak能访问到你的本地服务(本地开发可使用内网穿透工具)
四、本地业务服务接收示例
以下是Spring Boot接口示例,用于接收Keycloak推送的事件:
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController public class UserDisabledController { @PostMapping("/api/user/disabled-handler") public void handleUserDisabled(@RequestBody UserDisabledRequest request) { // 执行你的业务逻辑:记录日志、发送通知、冻结用户资源等 System.out.println("用户[" + request.getUsername() + "](" + request.getUserId() + ")被临时禁用,原因:" + request.getDisableReason()); // 此处添加具体业务操作,如更新数据库、调用内部服务等 } public static class UserDisabledRequest { private String userId; private String username; private String disableReason; // Getter & Setter public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getDisableReason() { return disableReason; } public void setDisableReason(String disableReason) { this.disableReason = disableReason; } } }
内容的提问来源于stack exchange,提问作者Самир Шахмурадлы
相关产品推荐
相关产品推荐

