Wcf 使用HTTPS以编程方式配置ServiceHost端点?

Wcf 使用HTTPS以编程方式配置ServiceHost端点?,wcf,servicehost,Wcf,Servicehost,我使用的是无文件激活,这是服务器端的完整web.config,它有两个端点: <?xml version="1.0" encoding="utf-8"?> <configuration> <configSections> <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSectio

我使用的是无文件激活,这是服务器端的完整web.config,它有两个端点:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="entityFramework" 
             type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" 
             requirePermission="false" />
  </configSections>
  <connectionStrings>
    <add name="RedStripe"
         connectionString="Data Source=S964;Initial Catalog=MyDatabase;Persist Security Info=True;User ID=sa;Password=***;MultipleActiveResultSets=True"
         providerName="System.Data.SqlClient" />
  </connectionStrings>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
  </system.web>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
      <parameters>
        <parameter value="mssqllocaldb" />
      </parameters>
    </defaultConnectionFactory>
    <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    </providers>
  </entityFramework>
  <system.web>
    <customErrors mode="Off"/>
  </system.web>
  <system.serviceModel>
    <serviceHostingEnvironment>
      <!-- where virtual .svc files are defined -->
      <serviceActivations>     
        <add service="Company.Project.Business.Services.AccountClassService" 
             relativeAddress="Account/AccountClassService.svc" 
             factory="Company.Project.WebHost.CustomServiceHostFactory"/>

        <add service="Company.Project.Business.Services.AccountService"
             relativeAddress="Account/AccountService.svc"
             factory="Company.Project.WebHost.CustomServiceHostFactory"/>

      </serviceActivations>
    </serviceHostingEnvironment>
  </system.serviceModel>
</configuration>
这是我的CustomServiceHost:

public class CustomServiceHost : ServiceHost
{        
    public CustomServiceHost(Type serviceType, Uri[] baseAddresses)
        : base(serviceType, baseAddresses)
    {
    }

    protected override void InitializeRuntime()
    {
        AddServiceDebugBehavior();
        AddWcfMessageLoggingBehavior();
        AddGlobalErrorHandlingBehavior();
        AddServiceCredentialBehavior();
        AddEndpoints();
        ConfigureThrottling();
        base.InitializeRuntime();
    }

    private void AddEndpoints()
    {
        var wsHttpBinding = WcfHelpers.ConfigureWsHttpBinding();

        foreach (Uri address in BaseAddresses)
        {
            var endpoint = new ServiceEndpoint(
                ContractDescription.GetContract(Description.ServiceType),
                wsHttpBinding, new EndpointAddress(address));

            AddServiceEndpoint(endpoint);

            //adding mex
            AddServiceMetadataBehavior();
            AddServiceEndpoint(
                ServiceMetadataBehavior.MexContractName,
                MetadataExchangeBindings.CreateMexHttpBinding(),
                address.AbsoluteUri + "/mex");

            break;
        }
    }
    private void AddGlobalErrorHandlingBehavior()
    {
        var errorHanlderBehavior = Description.Behaviors.Find<GlobalErrorBehaviorAttribute>();

        if (errorHanlderBehavior == null)
        {
            Description.Behaviors.Add(new GlobalErrorBehaviorAttribute(typeof(GlobalErrorHandler)));
        }
    }

    private void AddServiceCredentialBehavior()
    {
        var credentialBehavior = Description.Behaviors.Find<ServiceCredentials>();

        if (credentialBehavior == null)
        {
            var customAuthenticationBehavior = new ServiceCredentials();
            customAuthenticationBehavior.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
            customAuthenticationBehavior.UserNameAuthentication.CustomUserNamePasswordValidator = new CustomUserNamePasswordValidator();
            Description.Behaviors.Add(customAuthenticationBehavior);
        }
    }
    private void AddServiceDebugBehavior()
    {
        var debugBehavior = Description.Behaviors.Find<ServiceDebugBehavior>();

        if (debugBehavior == null)
        {
            Description.Behaviors.Add(
                new ServiceDebugBehavior() {IncludeExceptionDetailInFaults = true});
        }
        else
        {
            if (!debugBehavior.IncludeExceptionDetailInFaults)
                debugBehavior.IncludeExceptionDetailInFaults = true;
        }
    }
    private void AddServiceMetadataBehavior()
    {
        var metadataBehavior = Description.Behaviors.Find<ServiceMetadataBehavior>();

        if (metadataBehavior == null)
        {
            ServiceMetadataBehavior serviceMetadataBehavior = new ServiceMetadataBehavior();
            serviceMetadataBehavior.HttpsGetEnabled = true;
            Description.Behaviors.Add(serviceMetadataBehavior);
        }
    }
    private void AddWcfMessageLoggingBehavior()
    {
        var messageInspectorBehavior = Description.Behaviors.Find<WcfMessageInspector>();

        if (messageInspectorBehavior == null)
        {
            Description.Behaviors.Add(new WcfMessageInspector());
        }
    }
    private void ConfigureThrottling()
    {
        var throttleBehavior = Description.Behaviors.Find<ServiceThrottlingBehavior>();

        if (throttleBehavior != null) return;

        throttleBehavior = new ServiceThrottlingBehavior
        {
            MaxConcurrentCalls = 100,
            MaxConcurrentInstances = 100,
            MaxConcurrentSessions = 100
        };

        Description.Behaviors.Add(throttleBehavior);
    }
}
当我发布此WebHost项目并尝试浏览到以下两个地址之一时: 我得到以下错误:

提供的URI方案“http”无效;应为“https”。参数 名称:context.ListenUriBaseAddress

我注意到,在CustomServiceHost AddEndpoints()方法中,当在BaseAddresss上循环时,如果我硬编码一个地址,如下所示: 然后我可以成功浏览到它。当使用无文件激活和相对寻址时,如何构建基址?我可以在哪里指定他们使用https(他们现在似乎正在使用http)

提前谢谢


编辑1:这将解决问题,但似乎是一个彻底的攻击,我在哪里使用无文件激活指定https,以便使用https生成相对地址

var endpoint = new ServiceEndpoint(ContractDescription.GetContract(Description.ServiceType),
wsHttpBinding, new EndpointAddress(address.OriginalString.Replace("http:", "https:")));

编辑2:我想我正在了解这里发生了什么。谢谢你@Andreas K为我指明了正确的方向。如果我进入IIS并查看该站点的绑定,则有多个绑定,如图所示:

当在基地址上循环时,我在AddEndpoints()方法中放入了一些代码来写入数据库。当我尝试使用浏览器访问服务(如:)时,会在数据库中创建两个条目。

因此,似乎IIS站点绑定正在被拾取。但是,现在我不确定为什么数据库中没有更多的BaseAddresses条目。net.pipe、net.tcp等在哪里

试试这个:

WebHttpBinding binding = new WebHttpBinding();
binding.Security.Mode = WebHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None; // no password

// If you are not using IIS, you need to bind cert to port
Process proc = new Process();
proc.StartInfo.FileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.SystemX86), "netsh.exe");
proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
proc.StartInfo.Arguments =
    string.Format("http add sslcert ipport={0}:{1} certhash={2} appid={{{3}}}", ip, port, cert.Thumbprint, Guid.NewGuid());
proc.Start();
proc.WaitForExit();
要获得证书,请执行以下操作(注意证书必须在证书存储中):


这将在没有IIS的情况下工作。如果您使用的是IIS,则无需将证书绑定到端口(我想)

我在msdn上找到了以下内容:

无文件激活 尽管.svc文件使公开WCF服务变得容易,但更简单的方法是在Web.config中定义虚拟激活端点,从而完全消除对.svc文件的需要。 在WCF 4中,您可以在Web.config中定义映射到您的服务类型的虚拟服务激活端点。这使激活WCF服务成为可能,而无需维护物理.svc文件(也称为“无文件激活”)。以下示例显示如何配置激活端点:

<configuration>
  <system.serviceModel>
    <serviceHostingEnvironment>
      <serviceActivations>
        <add relativeAddress="Greeting.svc" service="GreetingService"/>
      </serviceActivations>
    </serviceHostingEnvironment>
  </system.serviceModel>
</configuration>

实际上磁盘上没有物理的.svc文件。图9显示了它在浏览器中的外观


我希望这能帮助您

事实证明,基本地址来自我的更新2中提到的IIS绑定,再次感谢@Andreas K为我指明了正确的方向。在IIS中,我有一个网站,下面有多个应用程序。我在这些绑定上同时启用了http和https。我已更新CustomServiceHost中的AddEndpoings()方法,如下所示:

private void AddEndpoints()
{
    var wsHttpBinding = WcfHelpers.ConfigureWsHttpBinding();

    foreach (var address in BaseAddresses.Where(a => a.Scheme == Uri.UriSchemeHttps))
    {
        var endpoint = new ServiceEndpoint(
            ContractDescription.GetContract(Description.ServiceType),
            wsHttpBinding, 
            new EndpointAddress(address));

        AddServiceEndpoint(endpoint);
        AddServiceMetadataBehavior();
    }
}

由于站点下的其他应用程序需要http,因此我的基本地址始终包含两个(http和https)。我需要手动过滤http,因为我不想为这个特定的站点公开它们。现在我知道他们是如何居住的,我很满意。谢谢大家。

你能在服务器端发布完整的
app.config
吗?你是说web.config?我更新了帖子以包含完整的服务器端web.configThank,应该足够了,在哪里?我必须使用WsHttpBinding和TransportWithMessageCredential,所以不确定这是如何应用的?是的,我正在使用IIS。你能用WebHttpBinding替换你的WsHttpBinding吗?并按照我提供的示例进行配置?好的,我将我的WsHttpBinding更改为使用WebHttpBinding,重新发布到IIS,并得到相同的错误。“提供的URI方案‘http’无效;应为‘https’。”从何处获取URI?这是你的web.config吗?确保您的URI以httpst开头,这是我的问题。我正在使用无文件激活,就像在帖子中所说的那样。我只是在标签中提供相对地址。我不知道基本地址是如何填充的,也不知道如何将它们更改为使用https。是的,我已经完成了所有这些工作,并且正在发布到IIS。在我的帖子中,您将看到我有一个serviceActivations部分,其中定义了两个服务,每个服务都有一个relativeAddress、service和factory值集。我的工厂正在使用Security.Mode=TransportWithMessageCredential和Security.Message.ClientCredentialType=MessageCredentialType.Username定义wsHttpBinding。这将导致架构为https,但在我的CustomServiceHost中,基本地址始终为http。如何将它们设置为https?另外,如果尚未设置,请参阅文章中的编辑1。我可以解决这个问题,但这似乎是一个黑客行为。你在IIS上托管你的wcf服务。因此,IIS提供了到“外部”的绑定。请检查一下这个装订。是的,编辑就像一个黑客:我的PIn IIS需要检查SSL并将ClientCertificates设置为“接受”。您可以看到我在OP中使用的绑定,除非有其他我不知道的绑定?是的,IIS有自己的绑定。我认为,如果您使用WCF服务的相对地址,url只是从IIS绑定+webapp+服务一起设置的。请在IIS控制台中查看此菜单:
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
cert = store.Certificates.Find(X509FindType.FindBySubjectName, certSubject, false)[0];
<configuration>
  <system.serviceModel>
    <serviceHostingEnvironment>
      <serviceActivations>
        <add relativeAddress="Greeting.svc" service="GreetingService"/>
      </serviceActivations>
    </serviceHostingEnvironment>
  </system.serviceModel>
</configuration>
private void AddEndpoints()
{
    var wsHttpBinding = WcfHelpers.ConfigureWsHttpBinding();

    foreach (var address in BaseAddresses.Where(a => a.Scheme == Uri.UriSchemeHttps))
    {
        var endpoint = new ServiceEndpoint(
            ContractDescription.GetContract(Description.ServiceType),
            wsHttpBinding, 
            new EndpointAddress(address));

        AddServiceEndpoint(endpoint);
        AddServiceMetadataBehavior();
    }
}