带有 KeyCloak 的多租户 Quarkus?
Multi-tenant Quarkus with KeyCloak?
我正在使用 Quarkus 框架编写一套服务。这些服务被设计为多租户的,并且应该使用 KeyCloak 进行保护。每个租户都有一个单独的 KeyCloak 安全领域,有自己的一组用户、组、角色等。
我发现 Quarkus guide to KeyCloak protection, explaining how to configure JAX-RS for authorization using KeyCloak. However, this guide assumes only 1 KeyCloak realm. I also found this example 展示了如何将 WAR 文件部署到 Wildfly,根据指定领域加载多个 KeyCloak 领域配置文件之一。
但是,尚不清楚此代码是否可以转换为 Quarkus。
是否可以通过这种方式在Quarkus中动态加载KeyCloak配置?有没有更好的方法来为这些 Quarkus 服务实现多租户安全性?
更新: 根据下面 Pedro 和 Shadov 的建议,我添加了一个非常简单的 KeycloakConfigResolver
实现并将其标记为 @ApplicationScoped
。但是,当我尝试启动 Quarkus 时,出现以下异常并且从未看到我的自定义 KeycloakConfigResolver
被调用:
17:53:55,340 INFO [io.qua.dep.QuarkusAugmentor] Beginning quarkus augmentation
17:53:55,758 INFO [org.jbo.threads] JBoss Threads version 3.0.0.Beta4
17:53:56,888 INFO [org.hib.Version] HHH000412: Hibernate Core {5.4.3.Final}
17:53:57,812 INFO [io.qua.dep.QuarkusAugmentor] Quarkus augmentation completed in 2472ms
17:53:57,967 ERROR [io.qua.dev.DevModeMain] Failed to start quarkus: java.lang.ExceptionInInitializerError
at java.base/java.lang.J9VMInternals.ensureError(J9VMInternals.java:193)
at java.base/java.lang.J9VMInternals.recordInitializationFailure(J9VMInternals.java:182)
at java.base/java.lang.J9VMInternals.newInstanceImpl(Native Method)
at java.base/java.lang.Class.newInstance(Class.java:2082)
at io.quarkus.runner.RuntimeRunner.run(RuntimeRunner.java:117)
at io.quarkus.dev.DevModeMain.doStart(DevModeMain.java:166)
at io.quarkus.dev.DevModeMain.main(DevModeMain.java:88)
Caused by: java.lang.RuntimeException: Failed to start quarkus
at io.quarkus.runner.ApplicationImpl1.<clinit>(ApplicationImpl1.zig:333)
... 5 more
Caused by: java.lang.RuntimeException: com.fasterxml.jackson.databind.exc.MismatchedInputException: No content to map due to end-of-input
at [Source: UNKNOWN; line: 1, column: 0]
at org.keycloak.adapters.KeycloakDeploymentBuilder.loadAdapterConfig(KeycloakDeploymentBuilder.java:198)
at org.keycloak.adapters.KeycloakDeploymentBuilder.build(KeycloakDeploymentBuilder.java:187)
at io.quarkus.keycloak.KeycloakTemplate.createKeycloakDeploymentContext(KeycloakTemplate.java:36)
at io.quarkus.deployment.steps.KeycloakAdapterProcessor$configureAdapter5.deploy_0(KeycloakAdapterProcessor$configureAdapter5.zig:47)
at io.quarkus.deployment.steps.KeycloakAdapterProcessor$configureAdapter5.deploy(KeycloakAdapterProcessor$configureAdapter5.zig:106)
at io.quarkus.runner.ApplicationImpl1.<clinit>(ApplicationImpl1.zig:207)
... 5 more
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: No content to map due to end-of-input
at [Source: UNKNOWN; line: 1, column: 0]
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)
at com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:4145)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4000)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3070)
at org.keycloak.adapters.KeycloakDeploymentBuilder.loadAdapterConfig(KeycloakDeploymentBuilder.java:196)
... 10 more
17:53:57,968 ERROR [io.qua.dev.DevModeMain] Failed to start Quarkus, attempting to start hot replacement endpoint to recover
17:53:58,003 INFO [org.xnio] XNIO version 3.7.2.Final
17:53:58,017 INFO [org.xni.nio] XNIO NIO Implementation Version 3.7.2.Final
我的自定义 KeycloakConfigResolver
是空的,保存一些日志语句。我从来没有看到我的 resolve
方法被调用或任何日志记录语句。下面是实现的样子:
@ApplicationScoped
public class MultiTenantKeycloakConfigResolver implements KeycloakConfigResolver {
/**
* Logger for this class
*/
private static final Logger logger = LoggerFactory.getLogger(MultiTenantKeycloakConfigResolver.class);
/*
* (non-Javadoc)
*
* @see
* org.keycloak.adapters.KeycloakConfigResolver#resolve(org.keycloak.adapters.
* spi.HttpFacade.Request)
*/
@Override
public KeycloakDeployment resolve(Request facade) {
if (logger.isDebugEnabled()) {
logger.debug("resolve(Request) - start"); //$NON-NLS-1$
}
if (logger.isInfoEnabled()) {
logger.info("resolve(Request) - HERE!!!"); //$NON-NLS-1$
}
// TODO Implement method
if (logger.isDebugEnabled()) {
logger.debug("resolve(Request) - end"); //$NON-NLS-1$
}
return null;
}
}
在 Spring-Boot 中是可能的,几乎与您发布的示例相同。只是从版本 4.6.0-Final
开始,他们添加了 class KeycloakSpringBootConfigResolverWrapper
来实际检查是否已经有任何 KeycloakConfigResolver
。在以前的版本中,它只是放置它自己的解析器。现在您所要做的就是注册一个自定义 KeycloakConfigResolver
bean,它就可以工作了。
我看到 Quarkus 中的 class 与 keycloak-spring-boot-adapters - https://github.com/quarkusio/quarkus/blob/master/extensions/keycloak/runtime/src/main/java/io/quarkus/keycloak/QuarkusKeycloakConfigResolver.java 非常相似。代码很明显,不用解释了。
由于我不熟悉 Quarkus,我不能 100% 确定它会起作用,但该字段带有 Inject
注释,因此建议您可以提供自己的解析器,在 Spring-Boot 和您发布的示例中尽可能采用相同的方式。
@Shadov,是的。你需要一个 KeycloakConfigResolver
.
使用 Quarkus,您只需创建一个实现 KeycloakConfigResolver
的 class。类似于 this.
我会更新指南并提供一些相关参考。
我正在使用 Quarkus 框架编写一套服务。这些服务被设计为多租户的,并且应该使用 KeyCloak 进行保护。每个租户都有一个单独的 KeyCloak 安全领域,有自己的一组用户、组、角色等。
我发现 Quarkus guide to KeyCloak protection, explaining how to configure JAX-RS for authorization using KeyCloak. However, this guide assumes only 1 KeyCloak realm. I also found this example 展示了如何将 WAR 文件部署到 Wildfly,根据指定领域加载多个 KeyCloak 领域配置文件之一。
但是,尚不清楚此代码是否可以转换为 Quarkus。
是否可以通过这种方式在Quarkus中动态加载KeyCloak配置?有没有更好的方法来为这些 Quarkus 服务实现多租户安全性?
更新: 根据下面 Pedro 和 Shadov 的建议,我添加了一个非常简单的 KeycloakConfigResolver
实现并将其标记为 @ApplicationScoped
。但是,当我尝试启动 Quarkus 时,出现以下异常并且从未看到我的自定义 KeycloakConfigResolver
被调用:
17:53:55,340 INFO [io.qua.dep.QuarkusAugmentor] Beginning quarkus augmentation
17:53:55,758 INFO [org.jbo.threads] JBoss Threads version 3.0.0.Beta4
17:53:56,888 INFO [org.hib.Version] HHH000412: Hibernate Core {5.4.3.Final}
17:53:57,812 INFO [io.qua.dep.QuarkusAugmentor] Quarkus augmentation completed in 2472ms
17:53:57,967 ERROR [io.qua.dev.DevModeMain] Failed to start quarkus: java.lang.ExceptionInInitializerError
at java.base/java.lang.J9VMInternals.ensureError(J9VMInternals.java:193)
at java.base/java.lang.J9VMInternals.recordInitializationFailure(J9VMInternals.java:182)
at java.base/java.lang.J9VMInternals.newInstanceImpl(Native Method)
at java.base/java.lang.Class.newInstance(Class.java:2082)
at io.quarkus.runner.RuntimeRunner.run(RuntimeRunner.java:117)
at io.quarkus.dev.DevModeMain.doStart(DevModeMain.java:166)
at io.quarkus.dev.DevModeMain.main(DevModeMain.java:88)
Caused by: java.lang.RuntimeException: Failed to start quarkus
at io.quarkus.runner.ApplicationImpl1.<clinit>(ApplicationImpl1.zig:333)
... 5 more
Caused by: java.lang.RuntimeException: com.fasterxml.jackson.databind.exc.MismatchedInputException: No content to map due to end-of-input
at [Source: UNKNOWN; line: 1, column: 0]
at org.keycloak.adapters.KeycloakDeploymentBuilder.loadAdapterConfig(KeycloakDeploymentBuilder.java:198)
at org.keycloak.adapters.KeycloakDeploymentBuilder.build(KeycloakDeploymentBuilder.java:187)
at io.quarkus.keycloak.KeycloakTemplate.createKeycloakDeploymentContext(KeycloakTemplate.java:36)
at io.quarkus.deployment.steps.KeycloakAdapterProcessor$configureAdapter5.deploy_0(KeycloakAdapterProcessor$configureAdapter5.zig:47)
at io.quarkus.deployment.steps.KeycloakAdapterProcessor$configureAdapter5.deploy(KeycloakAdapterProcessor$configureAdapter5.zig:106)
at io.quarkus.runner.ApplicationImpl1.<clinit>(ApplicationImpl1.zig:207)
... 5 more
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: No content to map due to end-of-input
at [Source: UNKNOWN; line: 1, column: 0]
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)
at com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:4145)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4000)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3070)
at org.keycloak.adapters.KeycloakDeploymentBuilder.loadAdapterConfig(KeycloakDeploymentBuilder.java:196)
... 10 more
17:53:57,968 ERROR [io.qua.dev.DevModeMain] Failed to start Quarkus, attempting to start hot replacement endpoint to recover
17:53:58,003 INFO [org.xnio] XNIO version 3.7.2.Final
17:53:58,017 INFO [org.xni.nio] XNIO NIO Implementation Version 3.7.2.Final
我的自定义 KeycloakConfigResolver
是空的,保存一些日志语句。我从来没有看到我的 resolve
方法被调用或任何日志记录语句。下面是实现的样子:
@ApplicationScoped
public class MultiTenantKeycloakConfigResolver implements KeycloakConfigResolver {
/**
* Logger for this class
*/
private static final Logger logger = LoggerFactory.getLogger(MultiTenantKeycloakConfigResolver.class);
/*
* (non-Javadoc)
*
* @see
* org.keycloak.adapters.KeycloakConfigResolver#resolve(org.keycloak.adapters.
* spi.HttpFacade.Request)
*/
@Override
public KeycloakDeployment resolve(Request facade) {
if (logger.isDebugEnabled()) {
logger.debug("resolve(Request) - start"); //$NON-NLS-1$
}
if (logger.isInfoEnabled()) {
logger.info("resolve(Request) - HERE!!!"); //$NON-NLS-1$
}
// TODO Implement method
if (logger.isDebugEnabled()) {
logger.debug("resolve(Request) - end"); //$NON-NLS-1$
}
return null;
}
}
在 Spring-Boot 中是可能的,几乎与您发布的示例相同。只是从版本 4.6.0-Final
开始,他们添加了 class KeycloakSpringBootConfigResolverWrapper
来实际检查是否已经有任何 KeycloakConfigResolver
。在以前的版本中,它只是放置它自己的解析器。现在您所要做的就是注册一个自定义 KeycloakConfigResolver
bean,它就可以工作了。
我看到 Quarkus 中的 class 与 keycloak-spring-boot-adapters - https://github.com/quarkusio/quarkus/blob/master/extensions/keycloak/runtime/src/main/java/io/quarkus/keycloak/QuarkusKeycloakConfigResolver.java 非常相似。代码很明显,不用解释了。
由于我不熟悉 Quarkus,我不能 100% 确定它会起作用,但该字段带有 Inject
注释,因此建议您可以提供自己的解析器,在 Spring-Boot 和您发布的示例中尽可能采用相同的方式。
@Shadov,是的。你需要一个 KeycloakConfigResolver
.
使用 Quarkus,您只需创建一个实现 KeycloakConfigResolver
的 class。类似于 this.
我会更新指南并提供一些相关参考。