Spring SAML - 支持自定义 SAML 断言
Posted
技术标签:
【中文标题】Spring SAML - 支持自定义 SAML 断言【英文标题】:Spring SAML - support for customized SAML Assertion 【发布时间】:2016-11-06 23:20:37 【问题描述】:我们有一个产品有一个客户,当我们充当服务提供商并且 idp 在客户端时,我们使用 Spring Security SAML 为该客户实施了 SAML 流。
现在我们有另一个客户也希望使用 SAML 进行身份验证,并且我们希望同一个 SP 为该客户实施 SAML 流,第二个客户将有 2 个 SAML 流,一个用于移动设备,一个用于其他设备使用相同 IDP 的设备。两个客户的 IDP 不同。
问题
这两个客户之间存在一些差异,例如断言属性不同,成功认证的动作不同,目前我们提供自己的实现。
还可能会有更多的变化,比如不同的绑定等......
我的问题是什么是支持这种情况并能够扩展我的 SP 以支持更多 SAML 流的最佳选择/最佳实践,但断言属性和更多配置存在差异?
当我们使用 Spring SAML 时,我们应该为每种 SAML 风格使用不同的 Spring Security 上下文文件吗?
并行使用多个上下文时是否存在线程安全问题?
【问题讨论】:
【参考方案1】:我的问题是支持此类的最佳选择/最佳实践是什么 场景并能够扩展我的 SP 以支持更多 SAML 流 Assertion 属性和更多配置的差异?
要分支某些配置,例如断言属性,您需要创建单独的服务提供者。可以共享其他配置和服务。其他配置应该共享。例如,我使用单个自定义 SAMLUserDetailsService 实现,它从凭证中提取唯一的 EntityID,并使用它为每个 IDP 映射不同的 SAML 属性。
当我们使用 Spring SAML 时,我们应该使用不同的 Spring Security 每个 SAML 风格的上下文文件? 在中使用多个上下文时是否存在线程安全问题 并行?
我不建议单独运行多个安全上下文。根据我的经验,Spring SAML 中涉及很多配置,并且很有可能,您将不得不通过这种方式不必要地复制大量代码。
在 Spring SAML 中,存在为不同的服务提供者使用不同别名的概念。我已经为许多 IDP 设置了许多服务提供者,并且能够使用一个 Spring Security 上下文并在需要处理差异的地方实现自定义服务。我没有您的要求的完整列表,并且可能有一些根本无法在单个 Spring 安全上下文中完成,但我会等到确保情况如此,然后再采取该路线。
每个 IDP 之间具体需要有哪些不同?
我可以发布的代码是有限的,但我已经包含了我可以发布的代码。
入口点 URL - 如果您有多个 IDP 在配置中设置了别名,则入口点 URL 默认为
"/saml/login/alias/" +productAlias+ "?idp=" + entityId;
如果您在负载均衡器后面,您可以将其配置为将您想要的任何 URL 重写为客户的 URL。
绑定和断言 - 这些在每个服务提供者 metadata.xml 文件中进行配置,并且对于每个客户可能不同。真正的挑战是如何从经过身份验证的 SAML 请求中提取属性并以可用的形式获取它。
我不知道是否有更好的方法来做到这一点,但我的要求是为我配置的任何 IDP 提供任何可映射和可配置的绑定。为此,我实现了一个自定义SAMLUserDetailsService
。从传入服务的SAMLCredential
中,您可以使用credential.getRemoteEntityID()
为客户提取映射。从那里您需要从凭证中解析出属性。
为 Microsoft 和其他 IDP 解析 SAML 属性的示例
public class AttributeMapperImpl implements AttributeMapper
@Override
public Map<String, List<String>> parseSamlStatements(List<AttributeStatement> attributeList)
Map<String, List<String>> map = new HashMap<>();
attributeList.stream().map((statement) -> parseSamlAttributes(statement.getAttributes())).forEach((list) ->
map.putAll(list);
);
return map;
@Override
public Map<String, List<String>> parseSamlAttributes(List<Attribute> attributes)
Map<String, List<String>> map = new HashMap<>();
attributes.stream().forEach((attribute) ->
List<String> sList = parseXMLObject(attribute.getAttributeValues());
map.put(attribute.getName(), sList);
);
return map;
@Override
public List<String> parseXMLObject(List<XMLObject> objs)
List<String> list = new ArrayList<>();
objs.stream().forEach((obj) ->
if(obj instanceof org.opensaml.xml.schema.impl.XSStringImpl)
XSStringImpl xs = (XSStringImpl) obj;
list.add(xs.getValue());
else if(obj instanceof org.opensaml.xml.schema.impl.XSAnyImpl)
XSAnyImpl xs = (XSAnyImpl) obj;
list.add(xs.getTextContent());
);
return list;
@Override
public String parseSamlStatementsToString(Map<String, List<String>> map)
String values = "";
Iterator it = map.entrySet().iterator();
while (it.hasNext())
Map.Entry pair = (Map.Entry) it.next();
values += pair.getKey() + "=" + pair.getValue() + " ";
it.remove(); // avoids a ConcurrentModificationException
return values;
对成功/失败采取的行动 - 有许多可能的方法来做到这一点。我选择在控制器中使用单个端点,该端点可以访问所有请求成功后进入的会话。身份验证成功后,我可以退出会话,用户来自哪个 IDP,并相应地重定向它们。失败有点困难,因为完全有可能,而且某些失败可能会非常严重,以至于您不知道请求来自哪个 IDP(即,如果 saml 消息使用错误的证书签名)。
【讨论】:
idp 之间的区别是入口点 url、绑定、成功和失败的操作以及断言......你能分享一个演示它的示例代码吗?以上是关于Spring SAML - 支持自定义 SAML 断言的主要内容,如果未能解决你的问题,请参考以下文章
Spring SAML - 如何在 SP HTTP 请求上添加自定义字段?
打开和关闭 Spring Security SAML 的方法
Spring Boot/Angular整合Keycloak实现单点登录
使用 Spring Boot 拦截 SAML Http 请求