You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Keycloak时,如何将新用户保存至Spring Boot电商应用数据库?

同步Keycloak注册用户到应用Users表的几种可行方案

方案1:Keycloak事件监听器(推荐,实时同步)

利用Keycloak的扩展能力,监听用户注册事件,实时将用户数据同步到你的应用数据库。这种方式能保证数据一致性,是生产环境的首选方案。

实现步骤:

  • 创建Keycloak扩展项目,实现EventListenerProvider和EventListenerProviderFactory接口
  • 监听Keycloak的REGISTER事件,提取用户核心信息(ID、用户名、邮箱等)
  • 通过HTTP调用Spring Boot的用户同步接口,或者直接通过JDBC写入应用的Users表

核心代码示例:

public class CustomRegistrationListener implements EventListenerProvider {
    private final KeycloakSession session;

    public CustomRegistrationListener(KeycloakSession session) {
        this.session = session;
    }

    @Override
    public void onEvent(Event event) {
        if (EventType.REGISTER.equals(event.getType())) {
            // 获取Keycloak用户详情
            UserModel keycloakUser = session.users().getUserById(event.getUserId(), session.getContext().getRealm());
            
            // 组装应用Users表需要的数据
            AppUser appUser = new AppUser();
            appUser.setKeycloakUserId(keycloakUser.getId());
            appUser.setUsername(keycloakUser.getUsername());
            appUser.setEmail(keycloakUser.getEmail());
            appUser.setCreatedAt(new Date());

            // 调用Spring Boot接口完成同步
            RestTemplate restTemplate = new RestTemplate();
            restTemplate.postForObject("http://your-spring-boot-service/api/users/sync", appUser, Void.class);
        }
    }

    @Override
    public void onEvent(AdminEvent event, boolean includeRepresentation) {}

    @Override
    public void close() {}
}

将代码打包成JAR,放到Keycloak的providers目录下,重启Keycloak后在控制台启用该监听器即可。

方案2:Spring Boot拦截首次登录请求同步

无需修改Keycloak,在用户首次通过Keycloak登录你的应用时,Spring Security获取用户信息后,检查应用Users表是否存在该用户,不存在则插入。

实现步骤:

  • 配置Spring Security集成Keycloak,确保能获取到认证后的用户信息
  • 自定义AuthenticationSuccessHandler,在登录成功后执行同步逻辑
  • 查询Users表,无匹配则插入新用户记录

核心代码示例:

@Component
public class LoginSyncHandler extends SimpleUrlAuthenticationSuccessHandler {
    private final UserRepository userRepository;

    public LoginSyncHandler(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        KeycloakAuthenticationToken authToken = (KeycloakAuthenticationToken) authentication;
        KeycloakPrincipal principal = (KeycloakPrincipal) authToken.getPrincipal();
        AccessToken accessToken = principal.getKeycloakSecurityContext().getToken();

        // 检查用户是否已存在于应用数据库
        Optional<AppUser> existingUser = userRepository.findByKeycloakUserId(accessToken.getSubject());
        if (existingUser.isEmpty()) {
            AppUser newUser = new AppUser();
            newUser.setKeycloakUserId(accessToken.getSubject());
            newUser.setUsername(accessToken.getPreferredUsername());
            newUser.setEmail(accessToken.getEmail());
            newUser.setCreatedAt(new Date());
            userRepository.save(newUser);
        }

        super.onAuthenticationSuccess(request, response, authentication);
    }
}

在Spring Security配置中绑定这个处理器:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
    private final LoginSyncHandler loginSyncHandler;

    public SecurityConfig(LoginSyncHandler loginSyncHandler) {
        this.loginSyncHandler = loginSyncHandler;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin().successHandler(loginSyncHandler);
    }

    // 其他Keycloak相关配置...
}

方案3:前端注册时双写(不推荐,存在一致性风险)

如果使用自定义Angular注册页面,可以在调用Keycloak注册接口成功后,再调用Spring Boot的用户创建接口。但这种方式存在数据不一致风险(比如Keycloak注册成功,但应用接口调用失败),仅适合测试或对一致性要求极低的场景。

Angular代码示例:

register(userForm: any) {
  // 先调用Keycloak注册接口
  this.keycloakService.register(userForm).then(() => {
    // 再调用应用的用户创建接口
    const userId = this.keycloakService.getKeycloakInstance().subject;
    this.http.post('/api/users', {
      keycloakUserId: userId,
      username: userForm.username,
      email: userForm.email
    }).subscribe({
      error: err => console.error('用户同步失败:', err)
    });
  }).catch(err => console.error('Keycloak注册失败:', err));
}

方案对比:

  • 方案1:实时同步,数据一致性高,但需要开发Keycloak扩展,有一定学习成本
  • 方案2:实现简单,无需修改Keycloak,但仅在用户首次登录时同步,存在延迟
  • 方案3:前端实现快速,但数据一致性无法保障,不适合生产环境

内容的提问来源于stack exchange,提问作者Pietro02

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 19:15:02