Keycloak v26.3.3自定义存储提供商联邦用户列表分页失效
自定义Keycloak用户存储SPI分页异常问题
我为本地部署的Keycloak实现了一个自定义存储提供商SPI,该SPI可正常激活。进入用户菜单选项时,能正常列出前x个用户,但点击下一页图标后,没有用户显示。
需要说明的是,我采用数据库端分页,但用户提供商的getUsersCount()方法从未被调用。
以下是我的用户提供商实现代码:
//package and imports removed for clarity public class ConsaludSdUserProvider implements UserStorageProvider, UserLookupProvider, UserQueryProvider, CredentialInputValidator, CredentialInputUpdater { private static final Logger LOG = Logger.getLogger(ConsaludSdUserProvider.class); private final KeycloakSession session; private final ComponentModel model; private final ISdUsuariosClient client; private final Argon2idEncryptor encryptor; protected Map<String, UserModel> loadedUsers = new HashMap<>(); public ConsaludSdUserProvider(KeycloakSession session, ComponentModel model) { this.session = session; this.model = model; this.client = new PostgresSdUsuariosClient(model.get(Constants.CONN_STRING_FIELD_NAME), model.get(Constants.USERID_FIELD_NAME), model.get(Constants.PASSWORD_FIELD_NAME)); this.encryptor = new Argon2idEncryptor(model.get(Constants.HASH_SECRET_FIELD_NAME)); } //region UserStorageProvider //- removed for clarity //endregion //region UserLookupProvider @Override public UserModel getUserById(RealmModel realmModel, String s) { LOG.info("getUserById invocado. ID: '" + s + "'"); try { var adapter = loadedUsers.get(s); if (adapter != null) { return adapter; } var external_id = getRutFromKeycloakId(s); var entity = client.getUsuarioByRut(external_id); if (entity == null) { LOG.info("No se encontró usuario con id: " + s); return null; } adapter = new DatosUsuarioUserAdapter(session, realmModel, model, entity); loadedUsers.put(s, adapter); return adapter; } catch (SdUsuariosClientException e) { LOG.error("Error al traer usuario por RUT", e); return null; } } @Override public UserModel getUserByUsername(RealmModel realmModel, String s) { LOG.info("getUserByUsername invocado. Username: '" + s + "'"); try { var adapter = loadedUsers.get(s); if (adapter != null) { return adapter; } var external_id = Integer.parseInt(s); var entity = client.getUsuarioByRut(external_id); if (entity == null) { LOG.info("No se encontró usuario con username: " + s); return null; } adapter = new DatosUsuarioUserAdapter(session, realmModel, model, entity); loadedUsers.put(s, adapter); return adapter; } catch (SdUsuariosClientException e) { LOG.error("Error al traer usuario por RUT", e); return null; } catch (NumberFormatException e) { LOG.error("Error al convertir ID proporcionado a un RUT", e); return null; } } @Override public CredentialValidationOutput getUserByCredential(RealmModel realm, CredentialInput input) { LOG.info("getUserByCredential invocado. Credential input: '" + input.getType() + "'"); return UserLookupProvider.super.getUserByCredential(realm, input); } @Override public UserModel getUserByEmail(RealmModel realmModel, String s) { LOG.info("getUserByEmail invocado. Email: '" + s + "'"); try { var entity = client.getUsuarioPorEmail(s); if (entity == null) { LOG.info("No se encontró usuario con email: " + s); return null; } return new DatosUsuarioUserAdapter(session, realmModel, model, entity); } catch (SdUsuariosClientException e) { LOG.error("Error al traer usuario por RUT", e); return null; } } //endregion //region UserQueryProvider @Override public Stream<UserModel> searchForUserStream(RealmModel realmModel, Map<String, String> params, Integer firstResult, Integer maxResults) { var search_param = params.get(UserModel.SEARCH); LOG.info("searchForUserStream invocado. search_param: '" + search_param + "', fila inicial: " + firstResult + ", max results: " + maxResults); try { var datos = client.getUsuarios(search_param, firstResult, maxResults); if (datos == null) { LOG.info("No se encontraron usuarios por el parámetro de búsqueda indicado: " + search_param); return Stream.empty(); } return datos.stream().map(r -> new DatosUsuarioUserAdapter(session, realmModel, model, r)); } catch (SdUsuariosClientException e) { LOG.info("Error al tratar de encontrar usuarios por parámetro de búsqueda: " + search_param, e); return Stream.empty(); } } @Override public Stream<UserModel> getGroupMembersStream(RealmModel realmModel, GroupModel groupModel, Integer firstResult, Integer maxResults) { LOG.info("getGroupMembersStream invocado. Fila inicial: " + firstResult + ", max results: " + maxResults); return Stream.empty(); } @Override public Stream<UserModel> searchForUserByUserAttributeStream(RealmModel realmModel, String attrName, String attrValue) { LOG.info("searchForUserByUserAttributeStream invocado. Nombre atributo: " + attrName + ", valor atributo: " + attrValue); throw new NotImplementedException(""); } @Override public int getUsersCount(RealmModel realm) { LOG.info("getUsersCount invocado."); try { return client.getUsuariosCount(); } catch (SdUsuariosClientException e) { LOG.error("Error al traer conteo de usuarios", e); return 0; } } @Override public int getUsersCount(RealmModel realm, Set<String> groupIds) { LOG.info("getUsersCount con groupIds invocado."); return UserQueryProvider.super.getUsersCount(realm, groupIds); } //endregion //region CredentialInputValidator //- removed for clarity //endregion //region CredentialInputUpdater //- removed for clarity //endregion private Integer getRutFromKeycloakId(String keycloakId) { var external_id = StorageId.externalId(keycloakId); return Integer.parseInt(external_id); } }
内容的提问来源于stack exchange,提问作者Léster
相关产品推荐
相关产品推荐

