JHipster7.8+Keycloak+React用户注册/登录无限重定向问题求助
问题分析与解决方案
问题现象
基于JHipster 7.8、Spring、React、Keycloak构建的应用,出现以下异常:
- 新用户注册时,调试模式下
UserService.syncUserWithIdP方法被持续调用,浏览器在localhost:9000和localhost:9000/oauth2/authorization/oidc之间无限重定向,最终跳转到http://localhost:9000/login?error。 - 调用
MyCustomSupplierRepository.save(supplier)保存用户额外数据时,登录环节也会触发相同错误。
核心原因排查
从提供的UserService.java代码中,定位到几个关键问题:
1. syncUserWithIdP中用户更新逻辑的缺陷
updateUser方法依赖SecurityUtils.getCurrentUserLogin()获取当前登录用户,但在用户同步阶段(syncUserWithIdP执行时),用户尚未完成完整认证流程,SecurityUtils.getCurrentUserLogin()会返回空值,导致用户数据更新失败。后续认证流程会认为用户数据未同步完成,反复触发syncUserWithIdP,最终引发重定向循环。
2. 用户权限为空的处理逻辑缺失
在getUserFromAuthentication方法中,当用户没有权限时,代码创建了USER权限,但未将其添加到用户的权限集合中,导致用户权限始终为空。Spring Security判定用户未通过认证,触发重定向到授权服务器,形成循环。
3. Supplier保存时的关联关系问题
fillSupplierWithNewData方法中设置了supplier.setUser(user),如果Supplier与User的关联配置(如级联操作、事务传播)不合理,保存Supplier时可能导致User数据状态不一致,进而触发认证时的反复同步。
修复方案
1. 重构syncUserWithIdP中的用户更新逻辑
直接操作已查询到的existingUser对象,替代依赖当前登录用户的updateUser方法:
// 替换syncUserWithIdP中更新用户的代码段 if (existingUser.isPresent()) { User dbUser = existingUser.get(); boolean needUpdate = false; // 判断是否需要更新用户数据 if (details.get("updated_at") != null) { Instant dbModifiedDate = dbUser.getLastModifiedDate(); Instant idpModifiedDate = (Instant) details.get("updated_at"); needUpdate = idpModifiedDate.isAfter(dbModifiedDate); } else { needUpdate = true; } if (needUpdate) { log.debug("Updating user '{}' in local database", user.getLogin()); dbUser.setFirstName(user.getFirstName()); dbUser.setLastName(user.getLastName()); if (user.getEmail() != null) { dbUser.setEmail(user.getEmail().toLowerCase()); } dbUser.setLangKey(user.getLangKey()); dbUser.setImageUrl(user.getImageUrl()); this.clearUserCaches(dbUser); // 无需额外调用save,@Transactional会自动提交修改 } } else { // 原新用户保存逻辑保持不变 log.debug("Saving user '{}' in local database", user.getLogin()); user = userRepository.save(user); this.clearUserCaches(user); }
2. 修复权限为空的处理逻辑
将创建的USER权限添加到用户的权限集合中:
// 修改getUserFromAuthentication中的权限处理代码 if (user.getAuthorities() == null || user.getAuthorities().size() == 0) { Authority auth = new Authority(); auth.setName(AuthoritiesConstants.USER); user.getAuthorities().add(auth); // 新增此行,将权限添加到用户集合 }
3. 优化Supplier与User的关联配置
- 确保
Supplier实体中user字段的关联注解配置正确:@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") private User user; - 在调用
supplierExtendedRepository.save(supplier)的方法上添加@Transactional注解,确保事务覆盖User和Supplier的操作,避免数据不一致。
验证步骤
- 重启应用,注册新用户,观察是否还会出现无限重定向。
- 保存用户的Supplier数据后,尝试登录,检查认证流程是否正常。
内容的提问来源于stack exchange,提问作者Galilo Galilo
相关产品推荐
相关产品推荐

