17

我正在编写 WCF 服务需要模拟和会话。

当我尝试在本地机器上调用它时没问题,但在远程机器上它总是失败并出现以下错误:

安全支持提供程序接口 (SSPI) 身份验证失败。服务器可能未在身份为“主机/主机名”的帐户中运行。如果服务器在服务帐户(例如网络服务)中运行,请将帐户的 ServicePrincipalName 指定为服务器的 EndpointAddress 中的标识。如果服务器在用户帐户中运行,请将帐户的 UserPrincipalName 指定为服务器的 EndpointAddress 中的标识。

如果我提供了一个 upn,它会抛出一个身份失败异常。

这是我的配置:

服务器配置(APP):

<system.serviceModel>    
    <behaviors>
      <serviceBehaviors>
        <behavior name="default">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
          <serviceAuthorization impersonateCallerForAllOperations="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <netTcpBinding>
        <binding name="DataService.netTcpBinding">
          <readerQuotas maxArrayLength="65535" maxBytesPerRead="2147483647" maxStringContentLength="2147483647"/>
          <reliableSession enabled="true" inactivityTimeout="24:00:00" ordered="true"/>          
          <security mode="TransportWithMessageCredential">
            <message clientCredentialType="Windows" />
            <transport clientCredentialType="Windows"/>          
          </security>
        </binding>
      </netTcpBinding>
    </bindings>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
    <services>
      <service behaviorConfiguration="default" name="DataService.DataService">
        <endpoint address="" binding="netTcpBinding" bindingConfiguration="DataService.netTcpBinding" 
          name="DataService.DataService" contract="DataService.IDataService"/>
        <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://address:4504/"/>
            <add baseAddress="net.tcp://address:4503/"/>
          </baseAddresses>
        </host>
      </service>
    </services>
</system.serviceModel>

客户端配置:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>     
        <bindings>
            <netTcpBinding>
                <binding name="DataService.DataService" closeTimeout="00:01:00"
                    openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
                    transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions"
                    hostNameComparisonMode="StrongWildcard" listenBacklog="10"
                    maxBufferPoolSize="524288" maxBufferSize="65536" maxConnections="10"
                    maxReceivedMessageSize="65536">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                    <reliableSession ordered="true" inactivityTimeout="24.00:00:00"
                        enabled="true" />
                    <security mode="TransportWithMessageCredential">
                        <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
                        <message clientCredentialType="Windows" algorithmSuite="Default" />
                    </security>
                </binding>
            </netTcpBinding>
        </bindings>
        <client>
            <endpoint address="net.tcp://address:4503/" binding="netTcpBinding"
                bindingConfiguration="DataService.DataService"
                contract="ataService.IDataService" name="DataService.DataService">
              <identity>
                <dns value="DOMAIN"/>                                                  
              </identity>
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>

任何帮助将不胜感激。

4

2 回答 2

23

Windows 服务使用用户主体名称或服务主体名称(文档)注册自己。引用该链接:“如果服务在 LocalSystem、LocalService 或 NetworkService 帐户下运行,则默认情况下会以 host/ 的形式生成服务主体名称 (SPN),因为这些帐户可以访问计算机的 SPN 数据。如果该服务在不同的帐户下运行,Windows Communication Foundation (WCF) 会以 @ 的形式生成一个 UPN。 ” 事实上,这句话与您的错误消息所述的内容非常相似。所以好像...

a) 如果服务在本地服务帐户或类似的标准帐户下运行,那么您需要调整您的客户端配置文件,其中实际服务器的名称是“地址”并且端点在端口 4503 上运行:

<identity>
     <servicePrincipalName value="host/address:4503" />
</identity>

b)另外,如果您在专用服务帐户下运行(我们在域“MyDomain”上将其称为“ServiceAccount”),那么您想要

<identity>
     <userPrincipalName value="ServiceAccount@MyDomain" />
</identity>

请注意,您可能需要在这两种情况下使用完全限定域名,包括森林和树级别。对于私有 LAN/WAN 内的简单域,这将意味着 address.MyDomain.local 和 ServiceAccount@MyDomain.local。如果您的域位于名为 MyTree 的树中,那么它将是 ServiceAccount@MyDomain.MyTree.local;如果那是在一个名为 MyForest 的森林中,那么它将是 Serviceaccount@MyDomain.MyTree.MyForest.local(与 ServicePrincipalName 类似)。使用 Kerberos 进行身份验证时需要完全限定名称。

于 2013-05-31T02:12:17.423 回答
8

还有一个肮脏的黑客,张贴在这里这里这里,并在这里分析。

您可以提供一个虚拟服务主体名称 (SPN)。在这种情况下,WCF 不会失败,但会退回到 NTLM 进行身份验证,而不会验证主体。

所以,配置:

    <identity>
      <servicePrincipalName value="dummy" >
    </identity>

并以编程方式

    EndpointIdentity identity = EndpointIdentity.CreateSpnIdentity("dummy");

使用通道工厂:

    Uri uri = new Uri("net.tcp://<myServer>:<myPort>/myServiceAddress");
    ChannelFactory channelFactory = new ChannelFactory<IMyContract>(new NetTcpBinding());
    channelFactory.CreateChannel(new EndpointAddress(uri, identity)

也将工作。

于 2013-11-08T01:01:49.810 回答