Spring security 如何在多租户环境中自动选择已配置的SAML标识提供程序,以使用Spring SAML执行SSO

Spring security 如何在多租户环境中自动选择已配置的SAML标识提供程序,以使用Spring SAML执行SSO,spring-security,spring-saml,Spring Security,Spring Saml,我在多租户应用程序中使用SpringSAML来提供SSO。不同的租户使用不同的URL访问应用程序,并且每个租户都配置了单独的身份提供程序。如何根据用于访问应用程序的url自动分配正确的身份提供程序 例如: 租户1: 承租人2: 我看到我可以向url()添加一个参数idp,SAMLContextProvider将选择具有该实体id的身份提供程序。我开发了一个数据库支持的元数据提供程序,它将租户主机名作为初始化参数,从链接到该主机名的数据库中获取该租户的元数据。现在我想我需要某种方法来迭代元数据提供

我在多租户应用程序中使用SpringSAML来提供SSO。不同的租户使用不同的URL访问应用程序,并且每个租户都配置了单独的身份提供程序。如何根据用于访问应用程序的url自动分配正确的身份提供程序

例如:

租户1:

承租人2:


我看到我可以向url()添加一个参数idp,SAMLContextProvider将选择具有该实体id的身份提供程序。我开发了一个数据库支持的元数据提供程序,它将租户主机名作为初始化参数,从链接到该主机名的数据库中获取该租户的元数据。现在我想我需要某种方法来迭代元数据提供程序,将元数据的entityId链接到主机名。不过,我不知道如何获取元数据的entityId。这将解决我的问题。

您可以看到如何从方法中的
元数据提供程序中解析可用的EntityID。请注意,通常每个提供程序都可以提供多个IDP和SP定义,而不仅仅是一个

或者,您可以使用自己的类进一步扩展
ExtendedMetadataDelegate
,包括您想要的任何附加元数据(如entityId),然后简单地将
MetadataProvider
重新键入到自定义类中,并在通过
MetadataManager
迭代数据时从中获取信息


如果我是你,我会采取一点不同的方法。我将扩展SAMLContextProviderImpl
,覆盖方法populatePeerEntityId,并在那里执行主机名/IDP的所有匹配。有关详细信息,请参阅。

在撰写本文时,Spring SAML的版本为1.0.1.FINAL。它不支持开箱即用的多租户。除了上面Vladimir的建议之外,我还找到了另一种实现多租户的方法。它非常简单直接,不需要任何Spring SAML类的扩展。此外,它在
cachingmetadatadatamanager
中利用了Spring SAML对别名的内置处理

在控制器中,从请求中捕获租户名称,并使用租户名称作为别名创建一个
ExtendedMetadata
对象。接下来,从
extendedmatadelegate
中创建一个
extendedmatadelegate
,并对其进行初始化。从中解析实体ID,并检查它们是否存在于
MetadataManager
中。如果它们不存在,请添加提供程序并刷新元数据。然后使用
getEntityIdForAlias()
MetadataManager
获取实体id

这是控制器的代码。以下是解释一些注意事项的注释:

@控制器
公共类SAMLController{
@自动连线
MetadataManager MetadataManager;
@自动连线
ParserPool ParserPool;
@RequestMapping(value=“/login.do”,method=RequestMethod.GET)
公共模型和视图登录(HttpServletRequest请求、HttpServletResponse响应、@RequestParam字符串租户名称)
抛出MetadataProviderException、ServletException、IOException{
//使用租户名称加载元数据url
字符串tenantMetadataURL=loadTenantMetadataURL(tenantName);
//已弃用的构造函数,需要更改
HTTPMetadataProvider HTTPMetadataProvider=新的HTTPMetadataProvider(租户MetadataURL,15000);
httpMetadataProvider.setParserPool(parserPool);
//使用租户名称作为别名创建扩展元数据
ExtendedMetadata=新的ExtendedMetadata();
metadata.setLocal(true);
metadata.setAlias(租户名称);
//创建元数据提供程序并初始化它
ExtendedMetadataDelegate metadataDelegate=新的ExtendedMetadataDelegate(httpMetadataProvider,元数据);
metadataDelegate.initialize();
//MetadataManager中的getEntityIdForAlias()只能在元数据提供程序之后调用
//添加并刷新元数据。否则,别名将映射为空
//下面的代码是一种迂回的方法,用于确定提供程序是否已经
//是否已添加。
//方法parseProvider()在MetadataManager中具有受保护的作用域,因此将其复制到此处
设置NewEntityId=parseProvider(metadataDelegate);
设置ExistingEntityId=metadataManager.GetIdEntityNames();
//如果元数据管理器中不存在一个或多个IDP实体ID,则假定它是新的提供者。
//如果总是添加一个不带此检查的提供程序,则refreshMetadata()中的initialize方法
//如果存在重复项,请忽略提供程序,但重复项仍会添加到列表中
//由于调用超类方法addMetadataProvider(),提供程序的数量可能是一个错误。
如果(!existingEntityId.containsAll(newEntityId)){
addMetadataProvider(metadataDelegate);
metadataManager.refreshMetadata();
}
字符串entityId=metadataManager.getEntityIdForAlias(租户名称);
返回新的ModelAndView(“重定向:/saml/login?idp=“+urlcoder.encode(entityId,“UTF-8”));
}
专用集parseProvider(MetadataProvider provider)引发MetadataProviderException{
Set result=new HashSet();
XMLObject对象=provider.getMetadata();
if(EntityDescriptor的对象实例){
addDescriptor(结果,(EntityDescriptor)对象);
}else if(EntitiesDescriptor的对象实例){
addDescriptors(结果,(EntityDescriptor)对象);
}
返回结果;
}
私有void addDescriptor(设置结果、EntitiesDescriptor描述符)引发MetadataProviderException{
if(descriptor.getEntityDescriptors()!=null){