(401) 未经授权的错误:WCF安全/绑定

10

我有一个WCF Web服务和一个客户端都在同一台机器上。直接使用浏览器访问WCF Web服务可以正常工作,但是客户端无法连接,下面是错误信息。有什么想法吗?客户端和服务器都使用IIS中的集成Windows身份验证。

The remote server returned an error: (401) Unauthorized. 
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.Net.WebException: The remote server returned an error: (401) Unauthorized.

Source Error: 

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.  

Stack Trace: 

[WebException: The remote server returned an error: (401) Unauthorized.]
   System.Net.HttpWebRequest.GetResponse() +5313085
   System.ServiceModel.Channels.HttpChannelRequest.WaitForReply(TimeSpan timeout) +54

[MessageSecurityException: The HTTP request is unauthorized with client authentication scheme 'Negotiate'. The authentication header received from the server was 'Negotiate,NTLM'.]
   System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg) +7594687
   System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type) +275
   HRPaysService.IService1.GetAlert() +0
   HRPaysService.Service1Client.GetAlert() +15
   _Default.Page_Load(Object sender, EventArgs e) +138
   System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e) +14
   System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e) +35
   System.Web.UI.Control.OnLoad(EventArgs e) +99
   System.Web.UI.Control.LoadRecursive() +50
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +627

客户端:

  <system.serviceModel>
     <bindings> 
        <basicHttpBinding> 
           <binding name="basicBinding"> 
              <security mode="TransportCredentialOnly">
                  <transport clientCredentialType="Windows" 
                             proxyCredentialType="Windows" realm="" />
                  <message clientCredentialType="UserName" 
                           algorithmSuite="Default" />
              </security> 
           </binding> 
        </basicHttpBinding> 
     </bindings> 
     <client>
         <endpoint 
             address="http://hrpaysservice/service1.svc" 
             binding="basicHttpBinding"
             bindingConfiguration="basicBinding" 
             contract="HRPaysService.IService1">
         </endpoint>
     </client>
  </system.serviceModel>

服务器:

<system.serviceModel>
   <bindings> 
      <basicHttpBinding> 
         <binding name="basicBinding"> 
            <security mode="TransportCredentialOnly">
                <transport clientCredentialType="Windows" 
                           proxyCredentialType="Windows" realm="" />
                <message clientCredentialType="UserName" 
                         algorithmSuite="Default" />
            </security> 
         </binding> 
      </basicHttpBinding> 
   </bindings> 
   <client>
       <endpoint 
           address="http://hrpaysservice/service1.svc" 
           binding="basicHttpBinding"
           bindingConfiguration="basicBinding" 
           contract="HRPaysService.IService1">
       </endpoint>
</client>
</system.serviceModel>

你的客户是不是一个Silverlight应用程序?它们与ASP.NET或Winforms/WPF应用程序的工作方式有很大不同。 - marc_s
4个回答

5

当我试图通过向Windows Forms应用程序添加“服务引用”来访问托管在IIS上的WCF服务时,遇到了同样的错误。但是当客户端调用服务方法时,我得到了“未经授权401异常”。以下是我解决这个问题的方法:

(1) 我正在使用[wsHttpBinding],将其切换为[basicHttpBinding],如下所示,在WCF服务配置文件中:

    <system.serviceModel>
      <bindings>
          <basicHttpBinding>
              <binding name="BasicHttpEndpointBinding">
                  <security mode="TransportCredentialOnly">
                      <transport clientCredentialType="Windows" />
                  </security>
              </binding>
          </basicHttpBinding>
      </bindings>
      <services>
      <service behaviorConfiguration="ServiceBehavior"   name="IService1">
          <endpoint address="" binding="basicHttpBinding"
            bindingConfiguration="BasicHttpEndpointBinding"
            name="BasicHttpEndpoint" contract="IService1">
              <identity>
                  <dns value="localhost" />
              </identity>
          </endpoint>
          <endpoint address="mex" binding="mexHttpBinding"
              contract="IMetadataExchange" />
      </service>
  </services>
  <serviceHostingEnvironment aspNetCompatibilityEnabled="true"
    multipleSiteBindingsEnabled="true" />

(2) 在你的客户端应用程序中添加一个“服务引用”,并给它命名(我们将在以下步骤中使用该名称作为“ProxyCalssName”)。

(3) 调整客户端应用程序的 app.config 文件如下:

<system.serviceModel>
    <client>
        <endpoint address="your service URL"
            binding="basicHttpBinding" bindingConfiguration="basic" contract="ProxyClassName.ServiceName"
            name="default" />
    </client>
    <bindings>
        <basicHttpBinding>
            <binding name="basic">
                <security mode="TransportCredentialOnly">
                    <transport clientCredentialType="Windows" proxyCredentialType="None"
                        realm="" />
                    <message clientCredentialType="UserName" algorithmSuite="Default" />
                </security>
            </binding>
        </basicHttpBinding>
    </bindings>
</system.serviceModel>

(4) 在客户端应用程序的代码后台中:
        ProxyClassName.MyServiceName srv = new ProxyClassName.MyServiceName("default");
 //default is the name of the endpoint in the app.config file as we did.
    srv.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;

祝你好运,DigitalFox


感谢您的回复。第(4)步中的代码旨在模拟当前的Windows身份验证,这符合原始问题的要求。但是,我想访问远程WCF服务;您的客户端配置非常好,但代码已更改为使用AllowedImpersonationLevel = TokenImpersonationLevel.Delegation,并将srv.ClientCredentials.Windows.ClientCredential.Domain / .Username / .Password设置为适当的值。 - Emanuel
在尝试了几个小时后,我终于放弃了让wsHttpBinding工作,并尝试了这个建议,最终成功让服务工作了。虽然我理解wsHttpBinding不应该有任何问题,但我很想知道出了什么问题。 - Prethen

1

客户:

<system.serviceModel>
    <bindings>
        <wsHttpBinding>
                <binding name="WSHttpBinding_IService1" closeTimeout="00:01:00"
                    openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
                     bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
                    maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text"
                    textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
                        <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                        <reliableSession ordered="true" inactivityTimeout="00:10:00"
                        enabled="false" />
                    <security mode="Message">
                        <transport clientCredentialType="Windows" proxyCredentialType="None"
                        realm="" />
                        <message clientCredentialType="Windows" negotiateServiceCredential="true"
                        algorithmSuite="Default" establishSecurityContext="true" />
                    </security>
                </binding>
        </wsHttpBinding>
    </bindings>
    <client>
        <endpoint address="http://localhost:3097/Service1.svc" binding="wsHttpBinding"
            bindingConfiguration="WSHttpBinding_IService1" contract="HRPaysService.IService1"
            name="WSHttpBinding_IService1">
                <identity>
                    <dns value="localhost" />
                </identity>
        </endpoint>
    </client>
</system.serviceModel>

服务器:

    <system.serviceModel>
        <bindings> 
        <basicHttpBinding> 
                    <binding name="basicBinding"> 
                    <security mode="TransportCredentialOnly"> 
                            <transport clientCredentialType="Windows"/> 
                    </security> 
                </binding> 
            </basicHttpBinding> 
        </bindings> 
    <services>
            <service behaviorConfiguration="basicBehavior" name="WcfService1.Service1"> 
                <endpoint address="" binding="basicHttpBinding" contract="WcfService1.IService1" bindingConfiguration="basicBinding" /> 
                <endpoint address="mex" binding="basicHttpBinding" contract="IMetadataExchange" bindingConfiguration="basicBinding" /> 
        </service>
    </services>
    <behaviors> 
            <serviceBehaviors> 
                <behavior name="basicBehavior"> 
                    <serviceMetadata httpGetEnabled="true" /> 
                </behavior> 
            </serviceBehaviors> 
        </behaviors> 
</system.serviceModel>


5
为什么这可以解决问题?你改变了什么? - Gusdor

0
你的服务 Web 应用程序中是否设置了 CrossDomain.xml 文档?如果没有,请创建一个包含以下内容的文档 -
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
    <allow-http-request-headers-from domain="*" headers="*"/>
</cross-domain-policy>

假设您的服务在abc.com上,而您正在从xyz.com上的客户端发出调用,因此这里是从xyz.com到另一个域abc.com的调用。这是跨域问题,为了解决这个问题,我们需要创建crossdomain.xml文件,并将上述代码放入该文件中,并将该文件放置在abc.com服务器上您的服务主机的bin文件夹中。 - Ziggler

0
如果WCF服务的虚拟目录未配置为匿名访问,则应删除中的“mex”端点。
您发布了2个不同的配置集,并且似乎存在不匹配。您能否发布导致错误的配置?
您的第一个(最顶部的)客户端配置和最新的服务器配置(不包含mex部分)应该可以工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接