防火墙后面的 WCF WebService / IIS 托管和配置问题

Posted

技术标签:

【中文标题】防火墙后面的 WCF WebService / IIS 托管和配置问题【英文标题】:WCF WebService / IIS Hosting & Configuration Issue Behind a Firewall 【发布时间】:2010-10-22 01:02:10 【问题描述】:

我有一个简单的 WCF Web 服务。它托管在我们生产域中默认网站下的 IIS 上。 (本地地址:10.10.20.100)

默认情况下,此默认网站设置为端口 80 上的“所有未分配”IP:但是,我注意到这导致 WCF 服务使用服务器本地 DNS 名称生成它的 WSDL。即 wsdl 中的所有 URI 都是

http://myserver.subdomain.domain.com/.../...

这不好,因为我需要将此服务公开给不了解生产环境内部 DNS 的站点。而且这个特定的服务器没有外部 DNS 名称。只是一个外部 IP 地址...

我已经成功地将 IIS 中的设置从“所有未分配”更改为“10.10.20.100”

这会导致服务使用 URI 生成它的 WSDL

http://10.10.20.100/.../...

这对于子域和其他子域中的其他机器来说很好,但是我在这里卡住了。服务器外部 IP 地址 (1.2.3.4) 是通过一些 NAT/PAT 转换映射的,因此它没有在服务器 IP 设置中明确设置(即它不显示在 IP 配置下)

因此,如果我将 IIS 默认网站 IP 地址从“所有未分配”->“1.2.3.4”更改为内部地址,那么 WCF 服务将返回...

错误请求(无效主机名)

如果我在内部 IP 地址上配置 IIS,并尝试通过我得到的外部 IP 地址访问服务

No protocol binding matches the given address 
'http://1.2.3.4/TestService/Service.svc'. Protocol bindings are 
configured at the Site level in IIS or WAS configuration

有什么方法可以让 IIS/WCF 生成它的 WSDL URI,其外部 IP 地址未在服务器上明确配置?

在我将 WCF 服务从窗口中删除之前,请有人帮助我。

【问题讨论】:

这是个好问题。为什么像这样的问题没有得到支持,而是像“厌倦了编程 GUI/业务应用程序,下一步该做什么?”这样的问题。在几分钟内获得 4 个? 很遗憾,我无法回答您的问题 - 我只能投赞成票。 +1。 感谢到目前为止的答案,伙计们。我今天将通过覆盖 ServiceHost 来尝试 Steve 的建议,如果这不起作用,我将硬着头皮让系统操作员为我们的 API 服务器获取一个新的 FQDN。 这是解决此问题的解决方案***.com/a/9671430 【参考方案1】:

这是因为您没有设置主机标头。这似乎是一个非常普遍的问题,我一直遇到它。它生成的 uri 没有配置,它通过检查站点的主机头来查找正确的地址。即使它在虚拟目录中,您也需要转到父目录(在您的情况下为默认目录)并添加主机头。

如果您不知道该怎么做,请告诉我。

【讨论】:

我遇到了同样的问题,这个解决方案已经解决了,但我认为它只影响了 https 主机。也许不吧。我在 IIS 服务器上使用 adsutil.vbs 脚本添加了主机头。它强制 443 端口使用特定的主机名而不是默认主机名。 但是我知道你可以为不安全的绑定设置主机头,所以也许值得一试。 如何添加主机头? technet.microsoft.com/en-us/library/cc753195%28v=ws.10%29.aspx 提供设置主机头的方向【参考方案2】:

必须是 IP 地址而不是 FQDN?通过交换到 FQDN 并将其设置在站点的主机标头中,然后通过

绑定到它
cscript //nologo %systemdrive%\inetpub\adminscripts\adsutil.vbs set W3SVC/1/ServerBindings ":80:hostname.example.com"

然后回收应用程序池将在生成的 WSDL 中生成该主机名。您会从中受益 - 您可以设置一个内部 DNS 将该 FQDN 解析为内部 IP,以及一个外部 DNS 解析为您的防火墙 IP,然后相同的系统将无需任何更改即可工作。

【讨论】:

【参考方案3】:

我也许可以防止忍者暴力...如果我了解您的问题... 您可以手动指定服务应在 web.config 中使用的完整地址,而不是让 ServiceHost 为您计算出来。您必须设置服务的基地址:

 <service behaviorConfiguration="Behaviour1" name="Api.Poll">
    <endpoint address="soap" binding="basicHttpBinding" bindingConfiguration="soapBinding"
      contract="Api.IPoll" />
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
    <host>
      <baseAddresses>
        <add baseAddress="http://www.mydomain.com/Api" />
        <add baseAddress="http://10.10.20.30/Api" />
      </baseAddresses>
    </host>
  </service>

使用此方法,您的服务应接受指定的基地址,加上服务名称,如果您有额外的端点地址,则应接受。此外,您需要使用自定义 ServiceHostFactory 以编程方式设置基地址。见下文:

    public class ServiceHostFactory : System.ServiceModel.Activation.ServiceHostFactory

    protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
                            
        ServiceHost host;

        host = new ServiceHost(serviceType, baseAddresses[0]);

        return host;
    

最后,一旦您构建了 ServiceHostFactory 类,您必须通过编辑 .svc 文件中的标记将其连接到您的服务:

<%@ ServiceHost Language="C#" Debug="true" Service="Api.Poll" Factory="Api.ServiceHostFactory" CodeBehind="Poll.svc.cs" %>

【讨论】:

在 IIS 下托管时,web.config 的 BaseAddress 部分将被完全忽略。存在与否没有区别。 IIS 根据虚拟目录配置计算出地址。 ***.com/questions/56249/… 嗯...我以前在 IIS 下使用过它,但这是一个不同的场景,有多个基地址。我想知道您是否可以使用 ServiceHostFactory 以编程方式设置基地址。请参阅修改后的答案。 为这位史蒂夫干杯...这里是格林威治标准时间晚上 9 点,所以我会在我回到办公室并明天再来找你时试一试。你能澄清一下我在哪里创建这个类吗?我是否只需将其放入 App_Code 文件夹,然后让我的服务扩展它? 是的,你可以把它放在 App_Code 部分,这样就可以了。不过,还有最后一步将其连接起来,为此您必须修改 .svc 文件的标记。我已经更新了答案以包含它。【参考方案4】:

以下是在 IIS7 中更改标头的方法。如果有帮助,也可以把我的 web.config 放一些。

http://www.sslshopper.com/article-ssl-host-headers-in-iis-7.html

<?xml version="1.0"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />      
    <customErrors mode="Off"></customErrors>
  </system.web> 
  <system.serviceModel>
    <client/>
    <services>
      <service name="WcfService1.Service1"
behaviorConfiguration="MyServiceTypeBehaviors">
        <host>
        <baseAddresses>
          <add baseAddress="https://pws.sjukra.is/"/>        
        </baseAddresses>
        </host>
        <endpoint address="https://pws.sjukra.is/Service1.svc"
                  listenUri="/" 
        binding="wsHttpBinding"
        contract="WcfService1.IService1"
        bindingConfiguration="myBasicHttpBindingConfig"/>
        <endpoint contract="IMetadataExchange"
   binding="mexHttpsBinding"                   
   address="mex"/>
      </service>
    </services>
    <bindings>
      <wsHttpBinding>
        <binding name="myBasicHttpBindingConfig">
          <security mode="TransportWithMessageCredential">
            <transport clientCredentialType="Windows" />
            <message clientCredentialType="UserName"/>
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="MyServiceTypeBehaviors">
          <serviceMetadata httpsGetEnabled="true"/>
          <serviceDebug httpsHelpPageEnabled="true" includeExceptionDetailInFaults="true"/>
          <serviceCredentials type="System.ServiceModel.Description.ServiceCredentials">
            <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="WcfService1.Service1,WcfService1"/>
            <serviceCertificate findValue="pws.sjukra.is" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
              <clientCertificate>
                  <authentication certificateValidationMode="ChainTrust" revocationMode="NoCheck"/>
              </clientCertificate>            
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true"></serviceHostingEnvironment>
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>
</configuration>

【讨论】:

【参考方案5】:

尽管这是一个旧线程,但它实际上帮助我将一个项目从 VS Web Developer Express 迁移到包含 WCF 服务的 MonoDevelop。

Web 应用程序使用以下 URL 请求 WCF 服务定义的 javascript 接口:http://127.0.0.1:8080/path-to-service/service.svc/js,这给了我错误:没有协议绑定与给定地址匹配

受此线程的启发,我能够解决问题,因为使用 localhost 而不是 127.0.0.1 访问服务有效!通过对http://localhost:8080/path-to-service/service.svc/js 的请求,我得到了 JavaScript 接口。

实际上我的应用程序没有使用绝对 URL 来包含 JavaScript 接口,但是默认情况下,当从 MonoDevelop 启动应用程序时,它会使用 127.0.0.1 访问应用程序,因此从服务中调用包含 JavaScript 失败。

它仍然不完美,因为我无法使用 localhost 而不是 127.0.0.1 从 MonoDevelop 启动应用程序,因为 XSP 的配置只允许我指定 IP 地址,但至少我知道如何获取围绕它。

【讨论】:

【参考方案6】:

问题是,在更改端口或 IP 地址后,您需要重新启动您使用的应用程序池,因为它仍然拥有旧信息,直到它被回收。

【讨论】:

以上是关于防火墙后面的 WCF WebService / IIS 托管和配置问题的主要内容,如果未能解决你的问题,请参考以下文章

webservice与WCF

WCF探索之旅——WCF与WebService的异同

Webservice 和 WCF 的区别?

从 iOS 目标调用方法 webService (WCF) C)

在 VS 2010 中更新 WCF 服务对 WebService 问题的引用

WCF和webservice的区别