带有服务结构的 SOAP - Https 和 Http 绑定

Posted

技术标签:

【中文标题】带有服务结构的 SOAP - Https 和 Http 绑定【英文标题】:SOAP with service fabric - Https and Http binding 【发布时间】:2018-09-05 20:15:25 【问题描述】:

我目前正在开发一个服务结构应用程序,它将公开一个肥皂侦听器,该侦听器将被另一个应用程序使用

我一直收到错误提示

找不到与方案 https 匹配的基地址 具有绑定 CustomBinding 的端点。注册的基地址方案 是[]

这里是 CreateServiceInstanceListener 方法

protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
    
        var serviceInstanceListers = new List<ServiceInstanceListener>()
        
            new ServiceInstanceListener(context =>
            
                return CreateSoapListener(context);
            )
            ,
            new ServiceInstanceListener(context =>
            
                return CreateSoapHTTPSListener(context);
            ),
        ;
        return serviceInstanceListers;
    


    private static ICommunicationListener CreateSoapHTTPSListener(StatelessServiceContext context)
    
        string host = context.NodeContext.IPAddressOrFQDN;
        var endpointConfig = context.CodePackageActivationContext.GetEndpoint("SecureServiceEndpoint");
        int port = endpointConfig.Port;
        string scheme = endpointConfig.Protocol.ToString();

        string uri = string.Format(CultureInfo.InvariantCulture, "0://1:2/MyService/", scheme, host, port);
        var listener = new WcfCommunicationListener<IServiceInterface>(
            serviceContext: context,
            wcfServiceObject: new Service(),
            listenerBinding: new BasicHttpsBinding(BasicHttpsSecurityMode.Transport),
            address: new EndpointAddress(uri)
        );

        // Check to see if the service host already has a ServiceMetadataBehavior
        ServiceMetadataBehavior smb = listener.ServiceHost.Description.Behaviors.Find<ServiceMetadataBehavior>();
        // If not, add one
        if (smb == null)
        
            smb = new ServiceMetadataBehavior();
            smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
            smb.HttpsGetEnabled = true;
            smb.HttpsGetUrl = new Uri(uri);

            listener.ServiceHost.Description.Behaviors.Add(smb);
        
        return listener;
    

    private static ICommunicationListener CreateSoapListener(StatelessServiceContext context)
    
        string host = context.NodeContext.IPAddressOrFQDN;
        var endpointConfig = context.CodePackageActivationContext.GetEndpoint("ServiceEndpoint");
        int port = endpointConfig.Port;
        string scheme = endpointConfig.Protocol.ToString();

        string uri = string.Format(CultureInfo.InvariantCulture, "0://1:2/MyService/", scheme, host, port);
        var listener = new WcfCommunicationListener<IServiceInterface>(
            serviceContext: context,
            wcfServiceObject: new Service(),
            listenerBinding: new BasicHttpBinding(BasicHttpSecurityMode.None),
            address: new EndpointAddress(uri)
        );

        // Check to see if the service host already has a ServiceMetadataBehavior
        ServiceMetadataBehavior smb = listener.ServiceHost.Description.Behaviors.Find<ServiceMetadataBehavior>();
        // If not, add one
        if (smb == null)
        
            smb = new ServiceMetadataBehavior();
            smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
            smb.HttpGetEnabled = true;
            smb.HttpGetUrl = new Uri(uri);

            listener.ServiceHost.Description.Behaviors.Add(smb);
        
        return listener;
    

这里是 app.config(对不起,如果有无用的条目,我从现有的 WCF 应用程序中复制了它)

    <?xml version="1.0" encoding="utf-8"?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/>
  </startup>

  <system.web>
    <customErrors mode="On"></customErrors>
    <compilation debug="true" targetFramework="4.6.2"/>
    <httpModules>
      <add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web"/>
    </httpModules>
    <machineKey decryption="AES" decryptionKey="decryptionkey" validation="SHA1" validationKey="validationkey"/>
  </system.web>
  <system.serviceModel>
    <diagnostics wmiProviderEnabled="true">
      <messageLogging logEntireMessage="true" logKnownPii="true" logMalformedMessages="true" logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="true"/>
      <endToEndTracing propagateActivity="true" activityTracing="true" messageFlowTracing="true"/>
    </diagnostics>
    <bindings>
      <customBinding>
        <binding name="HubBinding">
          <security defaultAlgorithmSuite="Basic256Sha256Rsa15" allowSerializedSigningTokenOnReply="true" authenticationMode="MutualCertificateDuplex" securityHeaderLayout="Lax" messageProtectionOrder="EncryptBeforeSign" messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"/>
          <textMessageEncoding messageVersion="Default"/>
          <httpsTransport maxReceivedMessageSize="1073741824"/>
        </binding>
        <binding name="AuthorityCustomBinding">
          <security defaultAlgorithmSuite="Basic256Sha256Rsa15" allowSerializedSigningTokenOnReply="true" authenticationMode="MutualCertificateDuplex" securityHeaderLayout="Lax" messageProtectionOrder="EncryptBeforeSign" messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"/>
          <textMessageEncoding messageVersion="Default"/>
          <httpsTransport maxReceivedMessageSize="1073741824"/>
        </binding>
        <binding name="CustomBinding_IServiceInterface">
          <security/>
          <textMessageEncoding/>
          <httpsTransport/>
        </binding>

      </customBinding>
    </bindings>
    <services>
      <service name="MyApp.ProductServiceManufacturer" behaviorConfiguration="ManufacturerBehaviour">
        <endpoint address="" name="ManufacturerProductService" binding="customBinding" bindingConfiguration="HubBinding" contract="MyApp.IProductServiceV20161"/>
      </service>

    </services>
    <client>
      <endpoint address="https://serverurl:8088/IServiceInterface/Service.svc" behaviorConfiguration="HubManufacturerBehavior" binding="customBinding" bindingConfiguration="AuthorityCustomBinding" contract="Service.IServiceInterface" name="CustomBinding_IProductServiceManufacturerV20161">
        <identity>
          <dns value="ServerCert"/>
        </identity>
      </endpoint>

    </client>
    <behaviors>
      <endpointBehaviors>
        <behavior name="HubManufacturerBehavior">
          <clientCredentials>
            <clientCertificate findValue="XXXXXX" storeLocation="LocalMachine" storeName="My" x509FindType="FindByThumbprint"/>
            <serviceCertificate>
              <defaultCertificate findValue="XXXXXX" storeLocation="LocalMachine" storeName="My" x509FindType="FindByThumbprint"/>
            </serviceCertificate>
          </clientCredentials>
        </behavior>
        <behavior name="MyApp.ReportingServiceManufacturerAspNetAjaxBehavior">
          <enableWebScript/>
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="ManufacturerBehaviour">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
          <serviceCredentials>
            <serviceCertificate findValue="XXXXXX" storeLocation="LocalMachine" storeName="My" x509FindType="FindByThumbprint"/>
          </serviceCredentials>
          <serviceSecurityAudit auditLogLocation="Application" suppressAuditFailure="true" serviceAuthorizationAuditLevel="Failure" messageAuthenticationAuditLevel="Failure"/>
        </behavior>
        <behavior name="">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
    <extensions>
      <bindingElementExtensions>
        <add name="securityBindingElementExtension" type="MyApp.BindingExtensions.SecurityBindingElementExtension, MyApp"/>
      </bindingElementExtensions>
    </extensions>
    <protocolMapping>
      <add binding="basicHttpsBinding" scheme="http"/>
      <add binding="customBinding" scheme="https"/>
    </protocolMapping>
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true">
      <remove name="TelemetryCorrelationHttpModule"/>
      <add name="TelemetryCorrelationHttpModule" type="Microsoft.AspNet.TelemetryCorrelation.TelemetryCorrelationHttpModule, Microsoft.AspNet.TelemetryCorrelation" preCondition="integratedMode,managedHandler"/>
      <remove name="ApplicationInsightsWebTracking"/>
      <add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" preCondition="managedHandler"/>
    </modules>


    <directoryBrowse enabled="true"/>
    <validation validateIntegratedModeConfiguration="false"/>
  </system.webServer>
</configuration>

我做错了什么或代码遗漏了什么 任何帮助将不胜感激,因为我以前从未做过 WCF。 顺便说一句,当 WCF 应用程序部署在服务器上时,它使用相同的配置工作,但如果你想知道为什么我使用服务结构来做这件事,这不取决于我 :)

更新 考虑到LoekD's answer,我更新了我的 CreateSoapHTTPSListener 方法,广告如下所示:

private static ICommunicationListener CreateSoapHTTPSListener(StatelessServiceContext context)
    
        string host = context.NodeContext.IPAddressOrFQDN;
        var endpointConfig = context.CodePackageActivationContext.GetEndpoint("SecureServiceEndpoint");
        int port = endpointConfig.Port;
        string scheme = endpointConfig.Protocol.ToString();
        var binding = new BasicHttpsBinding(BasicHttpsSecurityMode.Transport);
        binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
        binding.MaxReceivedMessageSize = 1073741824;

        string uri = ConfigurationManager.AppSettings.Get("ProductManufacturerService");
        Tools.TraceMessage(uri);
        var listener = new WcfCommunicationListener<IProductServiceV20161>(
            serviceContext: context,
            wcfServiceObject: new ProductServiceManufacturer(),
            listenerBinding: binding,
            address: new EndpointAddress(uri)
        );
        listener.ServiceHost.Credentials.ServiceCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, ConfigurationManager.AppSettings.Get("ServiceCertificateThumbprint"));
        listener.ServiceHost.Credentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, ConfigurationManager.AppSettings.Get("ClientCertificateThumbprint"));

        // Check to see if the service host already has a ServiceMetadataBehavior
        ServiceMetadataBehavior smb = listener.ServiceHost.Description.Behaviors.Find<ServiceMetadataBehavior>();
        // If not, add one
        if (smb == null)
        
            smb = new ServiceMetadataBehavior();
            smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
            smb.HttpsGetEnabled = true;
            smb.HttpGetEnabled = false;
            smb.HttpsGetUrl = new Uri(uri);
            listener.ServiceHost.Description.Behaviors.Add(smb);
        
        return listener;
    

然后我收到一条错误消息:

服务包含多个具有不同 ContractDescriptions 的 ServiceEndpoint,每个都有 Name='IProductServiceV20161' 和 Namespace='namespaceurl/'

我猜是因为服务端点有两个定义,一个在 app.config 文件中,另一个在 .cs 文件中 我在 app.config 中评论了端点标签并且它起作用了。 但是,与使用 WCF 应用程序获得的文件相比,我获得的 wsdl 文件缺少一些条目。

更新 2: 如何为服务指定端点身份?是否可以使用自定义的 BindingElementExtensionElement 类?

【问题讨论】:

【参考方案1】:

确保服务清单中的端点声明为“HTTPS”类型,并将 SSL 证书添加到 WCF 服务主机。

您可以尝试更改此设置吗:

listenerBinding: new BasicHttpsBinding(BasicHttpsSecurityMode.Transport)

进入这个:

listenerBinding: binding

绑定定义为:

var binding = new BasicHttpsBinding(BasicHttpsSecurityMode.Transport)
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;

更多信息here.

并使用您的 SSL 证书配置服务主机:

listener.ServiceHost.Credentials.ServiceCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, "Certificate Thumbprint Here");

【讨论】:

【参考方案2】:

我将假设您使用这些 X 掩盖了证书的指纹,并且还将服务器名称更改为 serverurl 作为客户端端点地址。

基于这些假设,我将使用我在您的客户端端点中看到的值来向您展示您的服务端点配置中似乎缺少的内容。

为您的服务设置基地址有两种选择。第一个选项适用于一个服务端点,它是您配置中的一个简单修复;只需将地址添加到您的服务中。像这样:

  <service name="MyApp.ProductServiceManufacturer" behaviorConfiguration="ManufacturerBehaviour">
    <endpoint address="https://serverurl:8088/IServiceInterface/Service.svc" name="ManufacturerProductService" binding="customBinding" bindingConfiguration="HubBinding" contract="MyApp.IProductServiceV20161"/>
  </service>

第二个选项,它适用于多个主机标头,或者即使您有多个服务并希望将配置转换用于环境部署。此解决方案要求您单独添加基地址,然后仅引用端点地址中的 *.svc。像这样:

  <service name="MyApp.ProductServiceManufacturer" behaviorConfiguration="ManufacturerBehaviour">
    <endpoint address="Service.svc" name="ManufacturerProductService" binding="customBinding" bindingConfiguration="HubBinding" contract="MyApp.IProductServiceV20161"/>
    <host>
      <baseAddresses>
        <add baseAddress="https://serverurl:8088/IServiceInterface" />
      </baseAddresses>
    </host>
  </service>

尝试其中的任何一个,愉快的编码。

-TwistedStem

【讨论】:

你的假设是正确的,我会试一试并更新结果,非常感谢 现在我收到此错误:副本在 _nodetype1_2 上打开期间出现多次故障。 API 调用:IStatelessServiceInstance.Open();错误 = System.InvalidOperationException (-2146233079) 服务包含多个具有不同 ContractDescriptions 的 ServiceEndpoint,每个都有 Name='IProductServiceV20161' 和 Namespace='namespaceurl'。要么为 ContractDescriptions 提供唯一的名称和命名空间,要么确保 ServiceEndpoints 具有相同的 ContractDescription 实例。 我尝试从 app.config 文件中删除端点条目,我设法访问了 wsdl,但似乎没有考虑到 app.config 的所有配置(证书)跨度> 【参考方案3】:

对于那些从事类似工作的人,这是我最终得到的(并且效果很好):

protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
        
            var serviceInstanceListers = new List<ServiceInstanceListener>()
            
                new ServiceInstanceListener(context =>
                
                    //return CreateRestListener(context);
                    return CreateSoapHTTPSListener(context);
                ),
            ;
            return serviceInstanceListers;
        

        private static ICommunicationListener CreateSoapHTTPSListener(StatelessServiceContext context)
        
            var binding = new CustomBinding();
            AsymmetricSecurityBindingElement assbe = (AsymmetricSecurityBindingElement)SecurityBindingElement.CreateMutualCertificateBindingElement(
                           MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10);

            binding.Elements.Add(assbe);
            binding.Elements.Add(new TextMessageEncodingBindingElement());
            binding.Elements.Add(new HttpsTransportBindingElement());
            // Extract the STS certificate from the certificate store.
            X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
            store.Open(OpenFlags.ReadOnly);
            X509Certificate2Collection certs = store.Certificates.Find(X509FindType.FindByThumbprint, ConfigurationManager.AppSettings.Get("ClientCertificateThumbprint"), false);
            store.Close();
            var identity = EndpointIdentity.CreateX509CertificateIdentity(certs[0]);
            string uri = ConfigurationManager.AppSettings.Get("ServiceUri");

            var listener = new WcfCommunicationListener<IService>(
                serviceContext: context,
                wcfServiceObject: new Service(),//where service implements IService
                listenerBinding: binding,
                address: new EndpointAddress(new Uri(uri), identity)
            );

            listener.ServiceHost.Credentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, ConfigurationManager.AppSettings.Get("ServiceCertificateThumbprint"));
            listener.ServiceHost.Credentials.ServiceCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, ConfigurationManager.AppSettings.Get("ClientCertificateThumbprint"));

            // Check to see if the service host already has a ServiceMetadataBehavior
            ServiceMetadataBehavior smb = listener.ServiceHost.Description.Behaviors.Find<ServiceMetadataBehavior>();
            // If not, add one
            if (smb == null)
            
                smb = new ServiceMetadataBehavior();
                smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy12;
                smb.HttpsGetEnabled = true;
                smb.HttpGetEnabled = false;
                smb.HttpsGetUrl = new Uri(uri);
                listener.ServiceHost.Description.Behaviors.Add(smb);
            
            return listener;
        

【讨论】:

以上是关于带有服务结构的 SOAP - Https 和 Http 绑定的主要内容,如果未能解决你的问题,请参考以下文章

PHP - 发送带有方法参数的 SOAP Web 服务

带有 MVC 的 Spring Boot SOAP Web 服务

带有 Zeep 和 Python 的 SOAP 客户端中的不记名令牌授权标头

如何使用带有 SSL 的 httpclient 调用 SOAP Web 服务

如何使用带有xml的angular+8的soap asmx服务

WCF 客户端。客户证书。第一次调用成功,第二次调用失败