SAML 提供多租户,这意味着可以使用多个 Keycloak 领域保护单个目标应用程序 (WAR) 的安全。这些领域可以位于同一 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>