Keycloak 20.0.1自定义UserStorageProvider语言 - NullPointer创建EntityM



我们正在研究将Keycloak 13.0.1迁移到Keycloak 20.0.1。我们的应用程序将用户存储在外部postgresql数据库中,并通过自定义UserStorageProvider将其公开给Keycloak。在Keycloak 20中,我无法获得一个正常运行的EntityManagerFactory或EntityManager来为我们的UserStorageProvider供电。

CustomUserStorageProviderFactory:

public class CustomUserStorageProviderFactory implements UserStorageProviderFactory<CustomUserStorageProvider> {
private static final String PROVIDER_ID = "CUSTOM_ID";
@Override
public CustomUserStorageProvider create(KeycloakSession session, ComponentModel model) {
try {
log.info("Intializing user federation provider.");
EntityManagerFactory factory = EntityManagerFactoryHelper.getFactory();
CustomUserStorageProvider provider = new CustomUserStorageProvider(session, model, factory.createEntityManager());
return provider;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public String getId() {
return PROVIDER_ID;
}
}

EntityManagerFactoryHelper:

public class EntityManagerFactoryHelper {
private static String PERSISTENCE_UNIT_NAME = "customunit";
private static EntityManagerFactory factory;
static {
try {
factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public static EntityManagerFactory getFactory() {
return factory;
}
}

persistence . xml:

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="customunit">
<class>my.custom.Class</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:postgresql://database_url:5432/database_name" />
<property name="javax.persistence.jdbc.user" value="user" />
<property name="javax.persistence.jdbc.password" value="password" />
<property name="javax.persistence.provider" value="org.hibernate.jpa.HibernatePersistenceProvider" />
<property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver" />
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
<property name="hibernate.hbm2ddl.auto" value="none" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" />
</properties>
</persistence-unit>
</persistence>

我的自定义提供程序代码打包为JAR并放置在'providers'文件夹

当CustomUserStorageProviderFactory被初始化时,抛出以下错误:

java.lang.NullPointerException
at io.quarkus.hibernate.orm.runtime.FastBootHibernatePersistenceProvider.getEntityManagerFactoryBuilderOrNull(FastBootHibernatePersistenceProvider.java:172)
at io.quarkus.hibernate.orm.runtime.FastBootHibernatePersistenceProvider.createEntityManagerFactory(FastBootHibernatePersistenceProvider.java:66)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:80)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:55)
at my.package.EntityManagerFactoryHelper.<clinit>(EntityManagerFactoryHelper.java:14)
at my.package.CustomUserStorageProviderFactory.create(CustomUserStorageProviderFactory.java:22)
at my.package.CustomUserStorageProviderFactory.create(CustomUserStorageProviderFactory.java:10)

我也试过在quarkus中配置数据源。属性,然后引用persistence.xml中的数据源,但结果是相同的。

空指针的来源似乎是FastBootHibernatePersistenceProvider中的以下几行。这里RecordedState为null:

RecordedState recordedState = PersistenceUnitsHolder.popRecordedState(persistenceUnitName);
if (recordedState.isReactive()) {
throw new IllegalStateException(
"Attempting to boot a blocking Hibernate ORM instance on a reactive RecordedState");
}

当启用TRACE日志时,在keycloak服务器启动期间,我可以看到相同的代码在JPA启动线程中执行时没有任何问题或空指针。打开到配置的数据库的连接,并正确检查和注册实体。只有当我尝试初始化自定义提供程序并检索EntityManagerFactory时,才会出现错误。

配置中缺少什么以获得Keycloak 20的自定义实体管理器?

找到了一个可行的解决方案。关键是通过javaxis .enterprise.inject.spi. cdi解析CustomUserStorageProvider,并使用@Inject和@PersistenceUnit来自动连接EntityManager。

CustomUserStorageProviderFactory:

@Override
public CustomUserStorageProvider create(KeycloakSession session, ComponentModel model) {
try {
log.info("Intializing user federation provider.");
CustomUserStorageProvider provider = CDI.current().select(CustomUserStorageProvider.class).get();
provider.setComponentModel(model);
provider.setSession(session);
return provider;
} catch (Exception e) {
log.info("Error initializing user federation provider.", e);
e.printStackTrace();
throw new RuntimeException(e);
}
}

CustomUserStorageProvider:

@RequestScoped
@Unremovable
public class CustomUserStorageProvider implements UserStorageProvider, UserLookupProvider, UserQueryProvider, UserGroupMembershipFederatedStorage {
private KeycloakSession session;
private ComponentModel componentModel;
@Inject
@PersistenceUnit("customunit")
private EntityManager entityManager;
...
}

最新更新