wcf - 用于声明模式 xRM (Microsoft Dynamics CRM 2011) 的 Apache CXF 客户端?

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

我正在尝试为 Microsoft Dynamics CRM 2011(“xRM”)Web 服务(我认为它基于 WCF 4)创建一个 Apache CXF(2.7.5)客户端,其中 CRM 处于声明模式,以便 WSDL此 Web 服务指向 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: Content Type text/xml; charset=UTF-8 was sent to a service expecting application/soap+xml; charset=utf-8. The client and service bindings may be mismatched.



当 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>

所以没有import s 有一个 schemaLocation .这让 CXF 提示说

org.apache.cxf.wsdl11.WSDLRuntimeException: Part request defined as element {http://docs.oasis-open.org/ws-sx/ws-trust/200512}RequestSecurityToken which is not in the schema.



我发现了导致这种情况的原因:包含 RequestSecurityToken 的模式等在 mex SOAP 调用结果中,但在单独的 <wsx:MetadataSection Dialect="http://www.w3.org/2001/XMLSchema"> 中部分,其中的代码在 AbstractSTSClient完全无视。

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

现在我在下一点被阻止:xRM WSDL(格式化后)包含一个 Addresshttp://www.w3.org/2005/08/addressing/anonymous (见下文),这会以某种方式导致 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 身份验证?

最佳答案

我们终于让它工作了,不仅使用 "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 创建一个。这可以解决被忽略的问题 <wsx:MetadataSection Dialect="http://www.w3.org/2001/XMLSchema">部分。它还解决了我的问题中的寻址问题,该问题已在 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'。")

现在注册 stsClient在属性 SecurityConstants.STS_CLIENT 下的请求上下文中( "ws-security.sts.client" ), 设置请求上下文属性 SecurityConstants.USERNAME ,并在属性(property)SecurityConstants.CALLBACK_HANDLER注册 CallbackHandler处理结果 WSPasswordCallback并设置密码,您就可以开展业务了。除了。

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

(我们不知道这里是不是CXF 2.7.5太严格了,或者AD FS 2.0的WSDL是否不符合标准。)

此外,我们通过查看 <ms-xrm:AuthenticationPolicy> 实现了在 Windows 模式 xRM 和 claim 模式 xRM 系统之间动态切换。 xRM WSDL 安全策略中的元素,并检查是否 <ms-xrm:Authentication>包含 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 的下载版本,我们通过获取 SP12Constants.ISSUED_TOKEN 在同一个“输出拦截器”中“修复”了该 WSDL。断言,得到它的 .getIssuerEpr().getMetadata().getAny() ,然后是 {http://www.w3.org/2005/08/addressing}Address .结果类似于 http://example.com:12345/adfs/services/trust/mex .我们检索该 URL,解析 XML,添加 <wsp:Policy/>元素,将结果保存到文件中,并使用该文件的 URL 作为 STSClient 的 wsdlLocation。

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

关于wcf - 用于声明模式 xRM (Microsoft Dynamics CRM 2011) 的 Apache CXF 客户端?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24144629/

相关文章:

javascript - 为什么此代码不会在 Microsoft Dynamics CRM 2011 中使用 Odata 创建商机?

java - Apache CXF 策略异常(WS 安全)- 无法检测到安全配置

java - 接受任何名称作为 XML 元素 - JAXB、CXF

c# - 关闭客户端应用程序: existing connection was forcibly closed by the remote host时在服务器上记录ServiceModel错误

wcf - TLS 是否必须在发送之前加密整个文件?

c# - POCO 类未从 WCF 返回

java - HTTP 400 从 Apache HTTP 客户端执行多部分请求时

c# - azure 缓存性能不佳

javascript - 如何将选项列表添加到自定义实体的案例表单中(注意 : replace the normal lookup field)?

javascript - CRM 2011 电话格式