5.5. 编写应用程序
首先实施 /{tenant}
端点。正如您在以下源代码中看到的那样,它只是常规 Jakarta REST 资源:
package org.acme.quickstart.oidc; import jakarta.inject.Inject; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import org.eclipse.microprofile.jwt.JsonWebToken; import io.quarkus.oidc.IdToken; @Path("/{tenant}") public class HomeResource { /** * Injection point for the ID Token issued by the OIDC provider. */ @Inject @IdToken JsonWebToken idToken; /** * Injection point for the Access Token issued by the OIDC provider. */ @Inject JsonWebToken accessToken; /** * Returns the ID Token info. * This endpoint exists only for demonstration purposes. * Do not expose this token in a real application. * * @return ID Token info */ @GET @Produces("text/html") public String getIdTokenInfo() { StringBuilder response = new StringBuilder().append("<html>") .append("<body>"); response.append("<h2>Welcome, ").append(this.idToken.getClaim("email").toString()).append("</h2>\n"); response.append("<h3>You are accessing the application within tenant <b>").append(idToken.getIssuer()).append(" boundaries</b></h3>"); return response.append("</body>").append("</html>").toString(); } /** * Returns the Access Token info. * This endpoint exists only for demonstration purposes. * Do not expose this token in a real application. * * @return Access Token info */ @GET @Produces("text/html") @Path("bearer") public String getAccessTokenInfo() { StringBuilder response = new StringBuilder().append("<html>") .append("<body>"); response.append("<h2>Welcome, ").append(this.accessToken.getClaim("email").toString()).append("</h2>\n"); response.append("<h3>You are accessing the application within tenant <b>").append(accessToken.getIssuer()).append(" boundaries</b></h3>"); return response.append("</body>").append("</html>").toString(); } }
要从传入请求解析租户并将其映射到 application.properties
中的特定 quarkus-oidc
租户配置,请为 io.quarkus.oidc.TenantConfigResolver
接口创建一个实现,它可以动态解析租户配置:
package org.acme.quickstart.oidc; import jakarta.enterprise.context.ApplicationScoped; import org.eclipse.microprofile.config.ConfigProvider; import io.quarkus.oidc.OidcRequestContext; import io.quarkus.oidc.OidcTenantConfig; import io.quarkus.oidc.OidcTenantConfig.ApplicationType; import io.quarkus.oidc.TenantConfigResolver; import io.quarkus.oidc.runtime.OidcUtils; import io.smallrye.mutiny.Uni; import io.vertx.ext.web.RoutingContext; @ApplicationScoped public class CustomTenantResolver implements TenantConfigResolver { @Override public Uni<OidcTenantConfig> resolve(RoutingContext context, OidcRequestContext<OidcTenantConfig> requestContext) { String path = context.request().path(); if (path.startsWith("/tenant-a")) { String keycloakUrl = ConfigProvider.getConfig().getValue("keycloak.url", String.class); OidcTenantConfig config = new OidcTenantConfig(); config.setTenantId("tenant-a"); config.setAuthServerUrl(keycloakUrl + "/realms/tenant-a"); config.setClientId("multi-tenant-client"); config.getCredentials().setSecret("secret"); config.setApplicationType(ApplicationType.HYBRID); return Uni.createFrom().item(config); } else { // resolve to default tenant config return Uni.createFrom().nullItem(); } } }
在前面的实现中,租户是从请求路径解析的。如果没有租户推断出来,则返回 null
以指示应使用默认租户配置。
tenant-a
应用类型是 hybrid
;如果提供,它可以接受 HTTP bearer 令牌。否则,它会在需要身份验证时启动授权代码流。