如何通过Spring Boot将Keycloak用户同步至MongoDB并获取用户ID?
一、实时同步:Keycloak事件监听(推荐)
Keycloak自带事件监听机制,用户注册、更新时会触发对应事件,你可以自定义监听器把用户数据推送到Spring Boot服务,再写入MongoDB。
实现步骤:
自定义Keycloak事件监听器
编写实现EventListenerProvider的Java类,监听REGISTER事件,提取用户的Keycloak ID、用户名、邮箱等信息,通过HTTP调用Spring Boot同步接口:public class CustomUserEventListener implements EventListenerProvider { private final HttpClient httpClient; private final String syncApiUrl; @Override public void onEvent(Event event) { if (EventType.REGISTER.equals(event.getType())) { UserModel user = session.users().getUserById(event.getUserId(), session.getContext().getRealm()); UserSyncDto dto = new UserSyncDto(); dto.setKeycloakId(user.getId()); dto.setUsername(user.getUsername()); dto.setEmail(user.getEmail()); // 调用Spring Boot同步接口 httpClient.post(syncApiUrl, dto); } } // 其他接口实现逻辑... }把这个类打包成JAR放到Keycloak的
providers目录,再在Keycloak后台启用该监听器。Spring Boot接收同步请求
写一个REST接口,接收Keycloak传来的用户数据,检查MongoDB中是否存在该Keycloak ID,不存在则创建新的User文档:@RestController @RequestMapping("/api/user-sync") public class UserSyncController { private final UserRepository userRepository; @PostMapping public ResponseEntity<Void> syncUser(@RequestBody UserSyncDto dto) { if (!userRepository.existsByKeycloakId(dto.getKeycloakId())) { User user = new User(); user.setKeycloakId(dto.getKeycloakId()); user.setUsername(dto.getUsername()); user.setEmail(dto.getEmail()); userRepository.save(user); } return ResponseEntity.ok().build(); } }
二、按需同步:Spring Security认证时自动创建
用户首次通过Keycloak认证访问资源服务器时,从JWT令牌中提取Keycloak用户ID,检查MongoDB中是否存在该用户,不存在则自动创建文档。
实现步骤:
配置Spring Security提取JWT信息
在资源服务器配置中启用JWT认证:@Configuration @EnableResourceServer public class ResourceServerConfig extends ResourceServerConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() .and() .oauth2ResourceServer().jwt(); } }拦截认证请求同步用户
通过@RestControllerAdvice拦截认证后的请求,提取并同步用户信息:@RestControllerAdvice public class UserSyncAdvice { private final UserRepository userRepository; @ModelAttribute public void syncCurrentUser(Authentication authentication) { if (authentication instanceof JwtAuthenticationToken) { Jwt jwt = ((JwtAuthenticationToken) authentication).getToken(); String keycloakId = jwt.getSubject(); String username = jwt.getClaimAsString("preferred_username"); String email = jwt.getClaimAsString("email"); userRepository.findByKeycloakId(keycloakId) .orElseGet(() -> { User user = new User(); user.setKeycloakId(keycloakId); user.setUsername(username); user.setEmail(email); return userRepository.save(user); }); } } }这种方式无需修改Keycloak,适合用户首次访问时自动初始化数据,缺点是用户不访问服务则不会同步数据。
三、定时同步:Keycloak Admin API批量拉取
如果需要同步历史用户或补全数据,可以用Keycloak Admin API定时拉取用户列表,对比MongoDB数据做更新或插入。
实现步骤:
添加Keycloak Admin依赖
在Spring Boot的pom.xml中引入官方客户端:<dependency> <groupId>org.keycloak</groupId> <artifactId>keycloak-admin-client</artifactId> <version>${keycloak.version}</version> </dependency>编写定时同步任务
使用Spring的@Scheduled注解定期调用Admin API拉取用户:@Service public class UserSyncService { private final Keycloak keycloak; private final UserRepository userRepository; @Scheduled(fixedRate = 3600000) // 每小时同步一次 public void syncUsers() { List<UserRepresentation> keycloakUsers = keycloak.realm("your-realm") .users() .list(); for (UserRepresentation kcUser : keycloakUsers) { userRepository.findByKeycloakId(kcUser.getId()) .ifPresentOrElse( existingUser -> { // 更新已有用户信息 existingUser.setUsername(kcUser.getUsername()); existingUser.setEmail(kcUser.getEmail()); userRepository.save(existingUser); }, () -> { // 创建新用户 User user = new User(); user.setKeycloakId(kcUser.getId()); user.setUsername(kcUser.getUsername()); user.setEmail(kcUser.getEmail()); userRepository.save(user); } ); } } }注意要在Keycloak后台创建一个拥有用户查询权限的服务账号,用于Admin API的身份认证。
四、可用工具/库
- Keycloak Event Listeners:官方原生扩展机制,无需额外依赖,定制性强。
- Keycloak Admin Client:官方提供的Java客户端,方便调用Admin API实现批量同步。
- Spring Security OAuth2 Resource Server:配合JWT自动提取用户信息,实现按需同步,无需额外工具。
内容的提问来源于stack exchange,提问作者strelok

