我正在尝试为 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(格式化后)包含一个
Address
的 http://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 Kuperus 解决方案的关键部分是我们创建自己的 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/