SAML 提供多租户功能,这意味着单个目标应用程序 (WAR) 可以使用多个 Keycloak 领域进行保护。这些领域可以位于同一个 Keycloak 实例上,也可以位于不同的实例上。
为此,应用程序必须具有多个 keycloak-saml.xml
适配器配置文件。
虽然您可以将 WAR 的多个实例与不同的适配器配置文件部署到不同的上下文路径,但这可能很不方便,您可能还希望根据上下文路径以外的其他内容来选择领域。
Keycloak 使创建自定义配置解析器成为可能,因此您可以选择为每个请求使用哪个适配器配置。在 SAML 中,配置仅在登录处理中很重要;用户登录后,会话将被身份验证,因此返回的 keycloak-saml.xml
是否不同并不重要。因此,对于同一个会话返回相同的配置是正确的方法。
要实现此目的,请创建一个 org.keycloak.adapters.saml.SamlConfigResolver
的实现。以下示例使用 Host
标头来定位正确的配置,并从应用程序的 Java 类路径加载配置及其相关元素
package example;
import java.io.InputStream;
import org.keycloak.adapters.saml.SamlConfigResolver;
import org.keycloak.adapters.saml.SamlDeployment;
import org.keycloak.adapters.saml.config.parsers.DeploymentBuilder;
import org.keycloak.adapters.saml.config.parsers.ResourceLoader;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.saml.common.exceptions.ParsingException;
public class SamlMultiTenantResolver implements SamlConfigResolver {
@Override
public SamlDeployment resolve(HttpFacade.Request request) {
String host = request.getHeader("Host");
String realm = null;
if (host.contains("tenant1")) {
realm = "tenant1";
} else if (host.contains("tenant2")) {
realm = "tenant2";
} else {
throw new IllegalStateException("Not able to guess the keycloak-saml.xml to load");
}
InputStream is = getClass().getResourceAsStream("/" + realm + "-keycloak-saml.xml");
if (is == null) {
throw new IllegalStateException("Not able to find the file /" + realm + "-keycloak-saml.xml");
}
ResourceLoader loader = new ResourceLoader() {
@Override
public InputStream getResourceAsStream(String path) {
return getClass().getResourceAsStream(path);
}
};
try {
return new DeploymentBuilder().build(is, loader);
} catch (ParsingException e) {
throw new IllegalStateException("Cannot load SAML deployment", e);
}
}
}
您还必须在 web.xml
中使用 keycloak.config.resolver
上下文参数配置要使用的 SamlConfigResolver
实现
<web-app>
...
<context-param>
<param-name>keycloak.config.resolver</param-name>
<param-value>example.SamlMultiTenantResolver</param-value>
</context-param>
</web-app>