0

我一直在试验 BizTalk 适配器包,特别是 SAP 的东西。

我能够直接使用 SAP 绑定连接到 SAP 并从客户端应用程序(例如控制台、Windows、网站)运行 RFC,完全没有问题。我还使用编排、发送端口、接收端口等在 BizTalk Server 2009 中完成了所有工作。

但是,我想将 SAP 功能作为标准 HTTP Web 服务向内部用户公开,而不要求他们在 PC 上安装 SAP 绑定内容。

因此,我在 Visual Studio 中创建了一个“WCF 适配器服务”项目,并按照向导进行操作。然后,我为客户端创建了一个标准 Web 应用程序,并使用“添加服务引用”添加了一个代理。一切正常,因为它找到了服务并添加了代理代码,但是当我尝试调用该服务时,我收到错误SapErrorMessage=Incomplete logon data

我不知道如何将 SAP 凭据从我的客户端 Web 应用程序传递到基本 HTTP 服务,然后传递到 SAP 绑定。如果我将 SAP 凭据放入 SAP 连接字符串中,一切正常,但这不是很安全,而且我想从客户端提供凭据(即要求用户提供他们的 SAP 凭据)。

在我见过的一些示例中,例如http://msdn.microsoft.com/en-us/library/dd788594(BTS.10).aspx,SAP凭据在 HTTP 标头中传递。不幸的是,我看到的所有示例都继续显示如何从 SharePoint 调用服务,其中有一个用于设置标题的对话框窗口。我没有使用 SharePoint!我尝试在客户端端点配置中添加“”部分,但这似乎不起作用。

那么,将 SAP 凭据传递给通过“WCF 适配器服务”向导创建的基本 HTTP Web 服务的推荐方法是什么?

有关信息,这是我的客户端 Web 应用程序上 web.config 的相关部分:

<system.serviceModel>
    <bindings>
        <basicHttpBinding>
            <binding name="RfcEndpoint" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true">
                <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384"/>
                <security mode="None">
                    <transport clientCredentialType="None" proxyCredentialType="None" realm=""/>
                    <message clientCredentialType="UserName" algorithmSuite="Default"/>
                </security>
            </binding>
        </basicHttpBinding>
    </bindings>
    <client>
        <endpoint address="http://xxxxxx/SAP_Service_1/Rfc.svc" binding="basicHttpBinding" bindingConfiguration="RfcEndpoint" contract="ServiceReference1.Rfc" name="RfcEndpoint">
            <headers>
                <SAP_Username>username</SAP_Username>
                <SAP_Password>password</SAP_Password>
            </headers>
        </endpoint>
    </client>
</system.serviceModel>

这是 WCF 适配器服务项目向导生成的 web.config:

<?xml version="1.0"?>
<configuration>
    <system.serviceModel>
        <services>
            <service behaviorConfiguration="customServiceBehavior" name="RfcClient">
                <endpoint address="" behaviorConfiguration="customEndpointBehavior" binding="basicHttpBinding" bindingConfiguration="RfcClientBindingConfig" name="RfcEndpoint" contract="Rfc"/>
                <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
            </service>
        </services>
        <behaviors>
            <endpointBehaviors>
                <behavior name="customEndpointBehavior">
                    <endpointBehavior usernameHttpHeader="SAP_Username" passwordHttpHeader="SAP_Password" adapterSecurityBridgeType="HTTPUsernamePassword"/>
                </behavior>
            </endpointBehaviors>
            <serviceBehaviors>
                <behavior name="customServiceBehavior">
                    <serviceMetadata httpsGetEnabled="true"/>
                    <serviceDebug includeExceptionDetailInFaults="true"/>
                    <serviceCredentials type="Microsoft.ServiceModel.Channels.AdapterServiceCredentials, Microsoft.ServiceModel.Channels, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
                    <serviceAuthorization serviceAuthorizationManagerType="Microsoft.ServiceModel.Channels.AdapterServiceAuthorizationManager, Microsoft.ServiceModel.Channels, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
                        <authorizationPolicies>
                            <add policyType="Microsoft.ServiceModel.Channels.AdapterAuthorizationPolicy, Microsoft.ServiceModel.Channels, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
                        </authorizationPolicies>
                    </serviceAuthorization>
                </behavior>
            </serviceBehaviors>
        </behaviors>
        <extensions>
            <behaviorExtensions>
                <add name="endpointBehavior" type="Microsoft.ServiceModel.Channels.AdapterEndpointBehavior, Microsoft.ServiceModel.Channels, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
            </behaviorExtensions>
        </extensions>
        <bindings>
            <basicHttpBinding>
                <binding name="RfcClientBindingConfig">
                    <security mode="None">
                        <transport clientCredentialType="None"/>
                        <message clientCredentialType="UserName"/>
                    </security>
                </binding>
            </basicHttpBinding>
            <sapBinding>
                <binding name="SAPBinding" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" enableBizTalkCompatibilityMode="false" receiveIdocFormat="Typed" enableSafeTyping="false" generateFlatFileCompatibleIdocSchema="true" maxConnectionsPerSystem="50" enableConnectionPooling="true" idleConnectionTimeout="00:15:00" flatFileSegmentIndicator="SegmentDefinition" enablePerformanceCounters="false" autoConfirmSentIdocs="false" acceptCredentialsInUri="true" padReceivedIdocWithSpaces="false" sncLibrary="" sncPartnerName="" rfcAllowStartProgram="">
                    <dataTypesBehavior datsMinToDateTime="NULL" datsMaxToDateTime="NULL" invalidDatsToDateTime="ERROR" emptyDatsToDateTime="0001-01-01T00:00:00" emptyTimsToDateTime="0001-01-01T00:00:00" dateTimeMaxToDats="99991231" dateTimeMinToDats="00010101" timsMaxToDateTime="NULL" invalidTimsToDateTime="ERROR" dateTimeMaxToTims="235959" dateTimeMinToTims="000000" invalidNumcToInt="0" emptyNumcToInt="0" dateTimeNullToDats="SKIP" dateTimeNullToTims="SKIP"/>
                </binding>
            </sapBinding>
        </bindings>
        <client>
            <endpoint address="sap://CLIENT=300;LANG=EN;@a/XXXXXX/00?RfcSdkTrace=False&amp;AbapDebug=False" binding="sapBinding" bindingConfiguration="SAPBinding" contract="Rfc" name="SAPBinding_Rfc"/>
        </client>
    </system.serviceModel>
</configuration>

我对 WCF 有点陌生,所以非常感谢任何帮助或指点!

谢谢

道格

4

3 回答 3

2

终于搞定了!

首先,我需要将 SAP 用户名和密码作为 HTTP 标头添加到请求中。正如一些人建议的那样,我首先尝试了编辑配置文件的简单解决方案:

<endpoint ....
  <headers>
    <HeaderName1>Header Value 1</HeaderName1>
    <HeaderName2>Header Value 2</HeaderName2>
  </headers>
</endpoint>

但这不会添加 HTTP 标头,或者至少我无法让它工作。

我在这里查看了优秀的文章http://ericphan.info/blog/2010/6/3/adding-custom-http-header-to-all-wcf-requests.html,它解释了如何使用 MessageInspector 添加传出请求的 HTTP 标头。它工作得很好,但头文件是在配置文件中定义的。我需要一种在代码中设置标题的方法。也许会有一种调整这段代码的方法,但我没那么聪明!

相反,我找到了一些其他示例并将其提炼为:

using (RfcClient client = new RfcClient("RfcEndpoint"))
{

    try
    {
        using (OperationContextScope scope = new OperationContextScope(client.InnerChannel))
        {

            HttpRequestMessageProperty httpRequestProperty;
            if (OperationContext.Current.OutgoingMessageProperties.ContainsKey(HttpRequestMessageProperty.Name))
            {
                httpRequestProperty = (HttpRequestMessageProperty)OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name];
            }
            else
            {
                httpRequestProperty = new HttpRequestMessageProperty();
            }
            httpRequestProperty.Headers.Add("SAP_Username", "dXNlcm5hbWU=");
            httpRequestProperty.Headers.Add("SAP_Password", "cGFzc3dvcmQ=");
            OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = httpRequestProperty;

            ret = client.BAPI_SALESORDER_GETLIST(cust, null, null, null, po, null, salesOrg, transGroup, ref orders);

            GridView1.DataSource = orders;
            GridView1.DataBind();

        }

        client.Close();

        Label1.Text = DateTime.Now.ToString();

    }
    catch (CommunicationException ex)
    {
        StringBuilder sb = new StringBuilder();
        sb.Append(ex.Message);
        if (ex.InnerException != null) sb.Append("~" + ex.InnerException.Message);
        Label1.Text = sb.ToString();
        client.Abort();
    }
    catch (TimeoutException ex)
    {
        Label1.Text = ex.Message;
        client.Abort();
    }
    catch (Exception ex)
    {
        Label1.Text = ex.Message;
        client.Abort();
        throw;
    }

}

为了完整起见,这是服务的 web.config 文件:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <system.serviceModel>
        <services>
            <service behaviorConfiguration="customServiceBehavior" name="RfcClient">
                <endpoint address="" behaviorConfiguration="customEndpointBehavior"
                    binding="basicHttpBinding" bindingConfiguration="RfcClientBindingConfig"
                    name="RfcEndpoint" contract="Rfc" />
                <endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange" />
            </service>
        </services>
        <behaviors>
            <endpointBehaviors>
                <behavior name="customEndpointBehavior">
                    <endpointBehavior usernameHttpHeader="SAP_Username" passwordHttpHeader="SAP_Password"
                        adapterSecurityBridgeType="HTTPUsernamePassword" />
                </behavior>
            </endpointBehaviors>
            <serviceBehaviors>
                <behavior name="customServiceBehavior">
                    <serviceMetadata httpsGetEnabled="true" />
                    <serviceDebug includeExceptionDetailInFaults="true" />
                    <serviceCredentials type="Microsoft.ServiceModel.Channels.AdapterServiceCredentials, Microsoft.ServiceModel.Channels, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
                        <serviceCertificate findValue="servername" storeLocation="LocalMachine"
                            storeName="My" x509FindType="FindBySubjectName" />
                    </serviceCredentials>
                    <serviceAuthorization serviceAuthorizationManagerType="Microsoft.ServiceModel.Channels.AdapterServiceAuthorizationManager, Microsoft.ServiceModel.Channels, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
                        <authorizationPolicies>
                            <add policyType="Microsoft.ServiceModel.Channels.AdapterAuthorizationPolicy, Microsoft.ServiceModel.Channels, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
                        </authorizationPolicies>
                    </serviceAuthorization>
                </behavior>
            </serviceBehaviors>
        </behaviors>
        <extensions>
            <behaviorExtensions>
                <add name="endpointBehavior" type="Microsoft.ServiceModel.Channels.AdapterEndpointBehavior, Microsoft.ServiceModel.Channels, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
            </behaviorExtensions>
        </extensions>
        <bindings>
            <basicHttpBinding>
                <binding name="RfcClientBindingConfig">
                    <security mode="Transport">
                        <transport clientCredentialType="None" />
                        <message clientCredentialType="UserName" />
                    </security>
                </binding>
            </basicHttpBinding>
            <sapBinding>
                <binding name="SAPBinding" closeTimeout="00:01:00" openTimeout="00:01:00"
                    receiveTimeout="00:10:00" sendTimeout="00:01:00" enableBizTalkCompatibilityMode="false"
                    receiveIdocFormat="Typed" enableSafeTyping="false" generateFlatFileCompatibleIdocSchema="true"
                    maxConnectionsPerSystem="50" enableConnectionPooling="true"
                    idleConnectionTimeout="00:15:00" flatFileSegmentIndicator="SegmentDefinition"
                    enablePerformanceCounters="false" autoConfirmSentIdocs="false"
                    acceptCredentialsInUri="false" padReceivedIdocWithSpaces="false"
                    sncLibrary="" sncPartnerName="" rfcAllowStartProgram="">
                    <dataTypesBehavior datsMinToDateTime="NULL" datsMaxToDateTime="NULL"
                        invalidDatsToDateTime="ERROR" emptyDatsToDateTime="0001-01-01T00:00:00"
                        emptyTimsToDateTime="0001-01-01T00:00:00" dateTimeMaxToDats="99991231"
                        dateTimeMinToDats="00010101" timsMaxToDateTime="NULL" invalidTimsToDateTime="ERROR"
                        dateTimeMaxToTims="235959" dateTimeMinToTims="000000" invalidNumcToInt="0"
                        emptyNumcToInt="0" dateTimeNullToDats="SKIP" dateTimeNullToTims="SKIP" />
                </binding>
            </sapBinding>
        </bindings>
        <client>
            <endpoint address="sap://CLIENT=300;LANG=EN;@a/XXXXXX/00?RfcSdkTrace=False&amp;AbapDebug=False"
                binding="sapBinding" bindingConfiguration="SAPBinding" contract="Rfc"
                name="SAPBinding_Rfc" />
        </client>
    </system.serviceModel>
</configuration>

对于 Web 客户端应用程序:

<system.serviceModel>
    <behaviors />
    <extensions />
    <bindings>
            <basicHttpBinding>
                <binding name="RfcEndpoint" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384"/>
                    <security mode="Transport">
                        <transport clientCredentialType="None" proxyCredentialType="None" realm=""/>
                        <message clientCredentialType="UserName" algorithmSuite="Default"/>
                    </security>
                </binding>
            </basicHttpBinding>
        </bindings>
        <client>
      <endpoint address="https://servername/SAP_Service_HTTP/Rfc.svc"
      binding="basicHttpBinding" bindingConfiguration="RfcEndpoint"
      contract="ServiceReference1.Rfc" name="RfcEndpoint" >
      </endpoint>>
    </client>
</system.serviceModel>

这使用传输安全 (SSL) 来保护标头,但它也可以正常工作。

注意练习的最后一个转折点。HTTP 标头值需要进行 Base64 编码!我不知道为什么,但一定是 SAP 适配器期望它们如此。

道格

于 2010-12-08T11:49:45.800 回答
0

找到了一个更简单的解决方案!

如果消息受到保护,WCF 很乐意在消息正文中发送用户名/密码凭据。不知道为什么我之前没有想到它,但是设置安全性是这样的:

<security mode="TransportWithMessageCredential">

意味着 Rohit 的建议现在有效。

因此,服务 web.config 需要消息中的凭据并使用传输安全性:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <system.serviceModel>
        <services>
            <service behaviorConfiguration="customServiceBehavior" name="RfcClient">
                <endpoint address="" behaviorConfiguration="customEndpointBehavior"
                    binding="basicHttpBinding" bindingConfiguration="RfcClientBindingConfig"
                    name="RfcEndpoint" contract="Rfc" />
                <endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange" />
            </service>
        </services>
        <behaviors>
            <endpointBehaviors>
                <behavior name="customEndpointBehavior">
                    <endpointBehavior adapterSecurityBridgeType="ClientCredentialUsernamePassword" />
                </behavior>
            </endpointBehaviors>
            <serviceBehaviors>
                <behavior name="customServiceBehavior">
                    <serviceMetadata httpsGetEnabled="true" />
                    <serviceDebug includeExceptionDetailInFaults="true" />
                    <serviceCredentials type="Microsoft.ServiceModel.Channels.AdapterServiceCredentials, Microsoft.ServiceModel.Channels, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
                        <serviceCertificate findValue="servername" storeLocation="LocalMachine"
                            storeName="My" x509FindType="FindBySubjectName" />
                    </serviceCredentials>
                    <serviceAuthorization serviceAuthorizationManagerType="Microsoft.ServiceModel.Channels.AdapterServiceAuthorizationManager, Microsoft.ServiceModel.Channels, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
                        <authorizationPolicies>
                            <add policyType="Microsoft.ServiceModel.Channels.AdapterAuthorizationPolicy, Microsoft.ServiceModel.Channels, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
                        </authorizationPolicies>
                    </serviceAuthorization>
                </behavior>
            </serviceBehaviors>
        </behaviors>
        <extensions>
            <behaviorExtensions>
                <add name="endpointBehavior" type="Microsoft.ServiceModel.Channels.AdapterEndpointBehavior, Microsoft.ServiceModel.Channels, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
            </behaviorExtensions>
        </extensions>
        <bindings>
            <basicHttpBinding>
                <binding name="RfcClientBindingConfig">
                    <security mode="TransportWithMessageCredential">
                        <transport clientCredentialType="None" />
                        <message clientCredentialType="UserName" />
                    </security>
                </binding>
            </basicHttpBinding>
            <sapBinding>
                <binding name="SAPBinding" closeTimeout="00:01:00" openTimeout="00:01:00"
                    receiveTimeout="00:10:00" sendTimeout="00:01:00" enableBizTalkCompatibilityMode="false"
                    receiveIdocFormat="Typed" enableSafeTyping="false" generateFlatFileCompatibleIdocSchema="true"
                    maxConnectionsPerSystem="50" enableConnectionPooling="true"
                    idleConnectionTimeout="00:15:00" flatFileSegmentIndicator="SegmentDefinition"
                    enablePerformanceCounters="false" autoConfirmSentIdocs="false"
                    acceptCredentialsInUri="false" padReceivedIdocWithSpaces="false"
                    sncLibrary="" sncPartnerName="" rfcAllowStartProgram="">
                    <dataTypesBehavior datsMinToDateTime="NULL" datsMaxToDateTime="NULL"
                        invalidDatsToDateTime="ERROR" emptyDatsToDateTime="0001-01-01T00:00:00"
                        emptyTimsToDateTime="0001-01-01T00:00:00" dateTimeMaxToDats="99991231"
                        dateTimeMinToDats="00010101" timsMaxToDateTime="NULL" invalidTimsToDateTime="ERROR"
                        dateTimeMaxToTims="235959" dateTimeMinToTims="000000" invalidNumcToInt="0"
                        emptyNumcToInt="0" dateTimeNullToDats="SKIP" dateTimeNullToTims="SKIP" />
                </binding>
            </sapBinding>
        </bindings>
        <client>
            <endpoint address="sap://CLIENT=300;LANG=EN;@a/XXXXXX/00?RfcSdkTrace=False&amp;AbapDebug=False"
                binding="sapBinding" bindingConfiguration="SAPBinding" contract="Rfc"
                name="SAPBinding_Rfc" />
        </client>
    </system.serviceModel>
</configuration>

和客户端 web.config 类似的配置:

<system.serviceModel>
    <bindings>
        <basicHttpBinding>
            <binding name="RfcEndpoint" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true">
                <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384"/>
                <security mode="TransportWithMessageCredential">
                    <transport clientCredentialType="None" proxyCredentialType="None" realm=""/>
                    <message clientCredentialType="UserName" algorithmSuite="Default"/>
                </security>
            </binding>
        </basicHttpBinding>
    </bindings>
    <client>
        <endpoint address="https://servername/SAP_Service_BASIC/Rfc.svc" binding="basicHttpBinding" bindingConfiguration="RfcEndpoint" contract="ServiceReference1.Rfc" name="RfcEndpoint"/>
    </client>
</system.serviceModel>

那么客户端代码很简单:

string cust = "12345";
string po = "12345";
string salesOrg = "1234";
string transGroup = "0";

BAPIORDERS[] orders = new BAPIORDERS[0];
BAPIRETURN ret = new BAPIRETURN();

using (RfcClient client = new RfcClient("RfcEndpoint"))
{
    client.ClientCredentials.UserName.UserName = "username";
    client.ClientCredentials.UserName.Password = "password";

    try
    {
        ret = client.BAPI_SALESORDER_GETLIST(cust, null, null, null, po, null, salesOrg, transGroup, ref orders);

        GridView1.DataSource = orders;
        GridView1.DataBind();

        Label1.Text = DateTime.Now.ToString();

        client.Close();

    }
    catch (CommunicationException ex)
    {
        StringBuilder sb = new StringBuilder();
        sb.Append(ex.Message);
        if (ex.InnerException != null) sb.Append("~" + ex.InnerException.Message);
        Label1.Text = sb.ToString();
        client.Abort();
    }
    catch (TimeoutException ex)
    {
        Label1.Text = ex.Message;
        client.Abort();
    }
    catch (Exception ex)
    {
        Label1.Text = ex.Message;
        client.Abort();
        throw;
    }

}

完美的!

于 2010-12-09T15:50:39.210 回答
0

将以下内容添加到客户端应用程序不适合您吗?

RfcClient client = new RfcClient();
client.ClientCredentials.UserName.UserName = "myusername";
client.ClientCredentials.UserName.Password = "mypassword";
于 2010-12-07T08:19:04.123 回答