【问题标题】:Spring SAML - support for customized SAML AssertionSpring SAML - 支持自定义 SAML 断言
【发布时间】: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 上下文文件吗?

并行使用多个上下文时是否存在线程安全问题?

【问题讨论】:

    标签: spring spring-security spring-saml


    【解决方案1】:

    我的问题是支持此类的最佳选择/最佳实践是什么 场景并能够扩展我的 SP 以支持更多 SAML 流 Assertion 属性和更多配置的差异?

    要分支某些配置,例如断言属性,您需要创建单独的服务提供者。可以共享其他配置和服务。其他配置应该共享。例如,我使用单个自定义 SAMLUserDetailsS​​ervice 实现,它从凭证中提取唯一的 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、绑定、成功和失败的操作以及断言......你能分享一个演示它的示例代码吗?
    猜你喜欢
    • 1970-01-01
    • 2015-08-19
    • 1970-01-01
    • 1970-01-01
    • 2017-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-16
    相关资源
    最近更新 更多