【问题标题】:Apache CXF client for claims-mode xRM (Microsoft Dynamics CRM 2011)?用于声明模式 xRM (Microsoft Dynamics CRM 2011) 的 Apache CXF 客户端?
【发布时间】:2014-07-31 10:20:16
【问题描述】:

我正在尝试为 CRM 处于声明模式的 Microsoft Dynamics CRM 2011 ("xRM") Web 服务(我理解为基于 WCF 4)创建一个 Apache CXF (2.7.5) 客户端,所以该 Web 服务的 WSDL 指向一个 STS(在我的例子中是 AD FS 2.0)。

我的主要问题:是否有任何教程、建议、博客文章可以帮助我(描述如何发送声明,或者如何避免它们而使用 Windows 身份验证)?

下面是我到目前为止所做的描述。


我已经为同一个 Web 服务提供了工作代码,当 CRM 处于 Windows 身份验证模式时该服务工作。该代码基于"CXF and MS CRM 2011" on Groovy Tom's Blog

为了支持声明模式,我还需要包含org.apache.cxf:cxf-rt-ws-mex,以便CXF 可以解析xRM WSDL。然后我需要让 CXF 内置的 STS 客户端使用 SOAP 1.2:

client.getRequestContext().put("ws-security.sts.client-soap12-binding", "true");

避免来自 AD FS 2.0 的错误 500。 (显然 AD FS 2.0 期望使用 SOAP 1.2 调用 /adfs/services/trust/mex 端点,而 CXF 默认为 SOAP 1.1。我必须从 AD FS's WCF trace 中找到这个,它报告了

System.ServiceModel.ProtocolException:内容类型文本/xml; charset=UTF-8 被发送到期望应用程序/soap+xml 的服务;字符集=utf-8。客户端和服务绑定可能不匹配。

当 Apache CXF 使用 SOAP 1.1 时。)

然后还有另一个问题:AD FS 的 /adfs/services/trust/mex 端点返回的 WSDL 似乎不完整,因为它包含

<wsdl:types>
    <xsd:schema
        targetNamespace="http://schemas.microsoft.com/ws/2008/06/identity/securitytokenservice/Imports">
        <xsd:import namespace="http://schemas.microsoft.com/Message" />
        <xsd:import namespace="http://schemas.xmlsoap.org/ws/2005/02/trust" />
        <xsd:import namespace="http://docs.oasis-open.org/ws-sx/ws-trust/200512" />
    </xsd:schema>
</wsdl:types>

所以imports 没有一个schemaLocation。这让CXF抱怨

org.apache.cxf.wsdl11.WSDLRuntimeException:部件请求定义为元素 {http://docs.oasis-open.org/ws-sx/ws-trust/200512}RequestSecurityToken 不在架构中。

我发现了造成这种情况的原因:包含 RequestSecurityToken 等的模式 在 mex SOAP 调用结果中,但在单独的 &lt;wsx:MetadataSection Dialect="http://www.w3.org/2001/XMLSchema"&gt; 部分中,AbstractSTSClient 中的代码完全忽略了这些部分.

所以我安装了我自己的 WSDLFactory+WSDLReader(使用属性 {{javax.wsdl.factory.WSDLFactory}}),它只是将三个命名空间的模式插入到它读取的任何 WSDL 中。

现在我在下一点被阻止:xRM WSDL(格式化后)包含http://www.w3.org/2005/08/addressing/anonymousAddress(见下文),这会导致 CXF 在 AD FS 的元数据中查找该端点。但是,这样的端点当然不存在:它包含例如https://...:.../adfs/services/trust/2005/usernamemixed

<wsdl:definitions
    targetNamespace="http://schemas.microsoft.com/xrm/2011/Contracts/Services"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
    xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
    snipped="other xmlns attributes">
    <wsp:Policy wsu:Id="CustomBinding_IOrganizationService_policy">
        <wsp:ExactlyOne>
            <wsp:All>
                <!-- snip -->
                <sp:EndorsingSupportingTokens
                    xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                    <wsp:Policy>
                        <sp:IssuedToken
                            sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
                            <Issuer
                                xmlns="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                                <Address xmlns="http://www.w3.org/2005/08/addressing">
                                    http://www.w3.org/2005/08/addressing/anonymous
                                </Address>

那我现在能做什么?


更一般地说,我现在的问题是:我是否走在为 xRM-in-claims-mode 构建 Java 客户端的正确道路上?其他人是如何让它工作的?或者有没有办法避免使用声明,而是使用带有 xRM-in-claims-mode 的 Windows 身份验证?

【问题讨论】:

    标签: wcf dynamics-crm-2011 cxf claims-based-identity adfs


    【解决方案1】:

    我们终于让它工作了,不仅使用了我在这个问题中提到的"CXF and MS CRM 2011" on Groovy Tom's Blog,还使用了"Using Apache CXF to connect to Microsoft Dynamics" by Jan-Hendrik Kuperus on the JH on Java blog(archive.org 缓存副本),此外还更正了(?)AD FS 2.0 WSDL .

    很遗憾,出于许可原因,我无法直接发布任何代码,但这里是我们所做工作的概述。


    Jan-Hendrik Ku​​perus 解决方案的关键部分是我们创建自己的 STSClient,而不是让 CXF 创建一个。这可以解决被忽略的&lt;wsx:MetadataSection Dialect="http://www.w3.org/2001/XMLSchema"&gt; 部分的问题。它还可以解决我的问题中的寻址问题,该问题已在 CXF 中继中得到修复。 (很遗憾,我们无法切换到最新的 CXF 版本:所有这些都是使用 CXF 2.7.5 完成的。)

    在该自定义 STS 客户端中,我们指向特定的 AD FS 端点,确保我们使用 SOAP 1.2(防止 HTTP 错误 500,请参阅问题),并关闭“更新”:

    STSClient stsClient = new STSClient(bus);
    stsClient.setSoap12();
    stsClient.setWsdlLocation(wsdlLocation.toExternalForm());
    stsClient.setServiceQName(new QName("http://schemas.microsoft.com/ws/2008/06/identity/securitytokenservice", "SecurityTokenService"));
    stsClient.setEndpointQName(new QName("http://schemas.microsoft.com/ws/2008/06/identity/securitytokenservice", "UserNameWSTrustBinding_IWSTrust13Async"));
    stsClient.setSendRenewing(false);
    

    (如果未关闭“更新”,则 AD FS 2.0 将返回 SOAP 错误“ID3035:请求无效或格式错误。”AD FS 跟踪显示“Microsoft.IdentityModel.SecurityTokenService.InvalidRequestException: MSIS3137 : RequestSecurityTokenElement 包含不受支持的 WS-Trust 参数:'Renewing'。")

    现在在属性SecurityConstants.STS_CLIENT ("ws-security.sts.client") 下的请求上下文中注册stsClient,设置请求上下文属性SecurityConstants.USERNAME,并在属性SecurityConstants.CALLBACK_HANDLER 中注册一个CallbackHandler 来处理生成的WSPasswordCallback 和设置密码,然后您就开始营业了。除了。

    除了那时我们发现 CXF 2.7.5 阻塞 AD FS 的 WSDL:KeyValueTokenBuilder#build() 中的java.lang.IllegalArgumentException: sp:KeyValueToken/wsp:Policy must have a value。事实证明,WSDL 包含许多具有属性wsp:Optional="true" 的安全策略,并且对于这些中的每一个,CXF 都需要一个嵌套的&lt;wsp:Policy&gt; 元素。所以我们所做的是对 AD FS WSDL 进行预处理,并在这些地方添加了空的 &lt;wsp:Policy/&gt; 元素。

    (我们不知道是CXF 2.7.5在这里过于严格,还是AD FS 2.0的WSDL没有遵循标准。)


    此外,我们通过查看 xRM WSDL 的安全策略中的 &lt;ms-xrm:AuthenticationPolicy&gt; 元素并检查 @987654339 是否在 Windows 模式 xRM 和声明模式 xRM 系统之间实现动态 @ 包含 ActiveDirectory 或联合。我们通过创建扩展XmlPrimitiveAssertion 的自定义策略扩展并在bus.getExtension(AssertionBuilderRegistry.class) 中注册相应的自定义构建器来做到这一点。然后我们在自定义的“输出拦截器”中创建自定义 STSClient:

    private static class XRMAuthSecurityModeInterceptor extends AbstractSoapInterceptor {
        public XRMAuthSecurityModeInterceptor() {
            super(Phase.PREPARE_SEND);
            addBefore("IssuedTokenOutInterceptor");
        }
    
        public void handleMessage(SoapMessage message) throws Fault {
            // if the custom assertion with security mode Federation is present, then create STSClient and...
                message.setContextualProperty(SecurityConstants.STS_CLIENT, stsClient);
        }
    }
    

    最后,由于我们不想使用 AD FS 的 WSDL 的下载版本,因此我们在同一个“输出拦截器”中“修复”了该 WSDL,方法是获取 SP12Constants.ISSUED_TOKEN 断言,获取它的 @987654344 @,然后是{http://www.w3.org/2005/08/addressing}Address。结果类似于http://example.com:12345/adfs/services/trust/mex。我们检索该 URL,解析 XML,如上所述添加 &lt;wsp:Policy/&gt; 元素,将结果保存到文件中,并将该文件的 URL 用作 STSClient 的 wsdlLocation。


    希望这对其他人有所帮助;如果您无法使用此功能,请随时提出问题。

    【讨论】:

    • 不要以为你可以澄清什么“所以我们所做的是预处理 AD FS WSDL,只是在这些地方添加了空的 元素意味着什么?我得到了同样的错误,已经卡了几天了。
    • 特别是在 /mex wsdl 中添加空 的位置。帮助非常感谢。
    • @AlanHollis 正如我所写,我需要在所有具有wsp:Optional="true" 属性的元素中直接添加&lt;wsp:Policy/&gt;。参见例如corp.sts.microsoft.com/adfs/services/trust/mex,您可以在其中看到&lt;mssp:RsaToken&gt;&lt;sp:KeyValueToken&gt; 等元素。
    • @AlanHollis 没问题,是的,完全正确,所有其他元素也是如此。
    • 感谢您提供 部分 - 它解决了“请求无效或格式错误。”
    【解决方案2】:

    我已将“匿名”问题的修复合并到 CXF:

    https://issues.apache.org/jira/browse/CXF-5807

    当颁发者地址为“匿名”时,您可以通过元数据指定所需的 STS 端点名称,否则它将退回到仅选择接收到的 WSDL 中的第一个端口。

    科尔姆。

    【讨论】:

    • +1 感谢您解决此问题!我真的没有选择切换到更新版本的 CXF,但很高兴知道它已被选中。为了完整起见,请注意在AbstractSTSClient 中仍然存在被忽略的&lt;wsx:MetadataSection Dialect="http://www.w3.org/2001/XMLSchema"&gt; 部分的问题。
    猜你喜欢
    • 1970-01-01
    • 2017-06-19
    • 2011-08-05
    • 1970-01-01
    • 2018-09-22
    • 1970-01-01
    • 1970-01-01
    • 2011-12-11
    • 1970-01-01
    相关资源
    最近更新 更多