使用 WCF 适配器服务项目进行 SAP 身份验证

发布于 2024-10-06 18:02:45 字数 7000 浏览 0 评论 0原文

我一直在尝试 BizTalk Adapter Pack,特别是 SAP 的东西。

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

但是,我希望将 SAP 功能作为标准 HTTP Web 服务向内部用户公开,而不要求他们拥有 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 有点陌生,因此非常感谢任何帮助或指示已收到!

谢谢

道格

I've been experimenting with the BizTalk Adapter Pack, specifically with the SAP stuff.

I'm able to connect to SAP and run an RFC from a client app (e.g. Console, Windows, Web site) using the SAP binding directly, no problem at all. I've also got it all working in BizTalk Server 2009 using orchestration, send port, receive port, etc.

However, I want to expose SAP functionality to internal users as a standard HTTP web service, without requiring them to have the SAP binding stuff installed on their PC.

So, I created a "WCF Adapter Service" project in Visual Studio, and followed the wizard. I then created a standard web app for the client end, and added a proxy using "Add service reference". It all worked OK, in that it found the service and added the proxy code, but when I try to invoke the service I get the error SapErrorMessage=Incomplete logon data.

What I don't know how to do is pass SAP credentials from my client web app to the Basic HTTP service and then on the the SAP binding. If I put the SAP credentials into the SAP connection string it all works OK, but this is not very secure and also I want to supply the credentials from the client (i.e. ask the user to provide their SAP credentials).

In some examples I've seen, e.g. http://msdn.microsoft.com/en-us/library/dd788594(BTS.10).aspx, the SAP credentials are passed in HTTP headers. Unfortunately, all the examples I've seen of this go on to show how to invoke the service from SharePoint, where there's a dialog window for setting the headers. I'm not using SharePoint! I've tried adding a "" section to my client endpoint configuration, but this didn't seem to work.

So, what is the recommended way to pass SAP credentials to a Basic HTTP web service created via the "WCF Adapter Service" wizard?

For information, this is the relevant part of the web.config on my client web app:

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

And this is the web.config generated by the WCF Adapter Service project wizard:

<?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&AbapDebug=False" binding="sapBinding" bindingConfiguration="SAPBinding" contract="Rfc" name="SAPBinding_Rfc"/>
        </client>
    </system.serviceModel>
</configuration>

I'm a bit new to WCF, so any help or pointers gratefully received!

Thanks

Doug

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

土豪 2024-10-13 18:02:45

终于可以工作了!

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

<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&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 适配器期望它们如此。

道格

Got it working at last!

First, I needed to get the SAP username and password added as HTTP headers in the request. I tried the simple solution of editing the config file first, as some have suggested:

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

But this does not add HTTP headers, or at least I wasn't able to get it to work.

I looked at the excellent article here http://ericphan.info/blog/2010/6/3/adding-custom-http-header-to-all-wcf-requests.html, which explains how to user a MessageInspector to add HTTP headers to the outgoing request. It works brilliantly, but headers are defined in the config file. I needed a way to set the headers in code. Perhaps there would be a way of adapting this code, but I'm not that clever!

Instead, I found some other examples and distilled it down to this:

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;
    }

}

For completeness, this is the web.config file for the service:

<?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&AbapDebug=False"
                binding="sapBinding" bindingConfiguration="SAPBinding" contract="Rfc"
                name="SAPBinding_Rfc" />
        </client>
    </system.serviceModel>
</configuration>

And for the web client app:

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

This uses transport security (SSL) to protect the headers, but it works without also.

Note the final twist to the exercise. The HTTP header values needed to be Base64 encoded! I've no idea why, but it must be that the SAP adapter is expecting them to be.

Doug

素罗衫 2024-10-13 18:02:45

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

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

<security mode="TransportWithMessageCredential">

意味着罗希特的建议现在起作用了。

因此,服务 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&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;
    }

}

完美!

Found an even easier solution!

WCF is happy to send the Username / Password credentials in the message body providing the message is protected. Don't know why I didn't think of it before, but setting security like this:

<security mode="TransportWithMessageCredential">

means that Rohit's suggestion now works.

So, the service web.config expects the credentials in the message and uses transport security:

<?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&AbapDebug=False"
                binding="sapBinding" bindingConfiguration="SAPBinding" contract="Rfc"
                name="SAPBinding_Rfc" />
        </client>
    </system.serviceModel>
</configuration>

And the client web.config is similarly configured:

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

Then the client code is simply:

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;
    }

}

Perfect!

〆凄凉。 2024-10-13 18:02:45

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

RfcClient client = new RfcClient();
client.ClientCredentials.UserName.UserName = "myusername";
client.ClientCredentials.UserName.Password = "mypassword";

Doesn't adding the following to the client app work for you?

RfcClient client = new RfcClient();
client.ClientCredentials.UserName.UserName = "myusername";
client.ClientCredentials.UserName.Password = "mypassword";
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文