WCFTestClient:HTTP请求使用客户端身份验证方案“匿名”未经授权。

77
我创建了一个 WCF 服务并将其部署在服务器上。当我浏览此服务时,它会给我带来带有 ?wsdl URL 的积极响应。现在我正在尝试通过 WCF 测试客户端测试服务。它显示适当的元数据。但是,当我尝试调用服务中的任何方法时,它会显示一个异常...以下是错误详细信息和堆栈跟踪...

HTTP 请求是未经授权的,客户端身份验证方案为“Anonymous”。从服务器接收到的身份验证标头为“Negotiate,NTLM”。

服务器堆栈跟踪:

at
System.ServiceModel.Channels.HttpChannelUtilities.ValidateAuthentication(HttpWebRequest request, HttpWebResponse response, WebException responseException, HttpChannelFactory factory)
HTTP 请求是未经授权的,客户端身份验证方案为“Anonymous”。从服务器接收到的身份验证标头为“Negotiate,NTLM”。

客户端绑定:

<bindings>
    <wsHttpBinding>
        <binding name="WSHttpBinding_IServiceMagicService" 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="None">
                <transport clientCredentialType="Windows" proxyCredentialType="None"
                    realm="" />
                <message clientCredentialType="Windows" negotiateServiceCredential="true"
                    establishSecurityContext="true" />
            </security>
        </binding>
    </wsHttpBinding>
</bindings>

服务器绑定:

<bindings>
  <wsHttpBinding>
    <binding name="WSHttpBinding_SEOService" closeTimeout="00:10:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:10:00" bypassProxyOnLocal="true" transactionFlow="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="999524288" maxReceivedMessageSize="655360000" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
      <readerQuotas maxDepth="32" maxStringContentLength="900000" maxArrayLength="900000" maxBytesPerRead="900000" maxNameTableCharCount="900000" />
      <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />
      <security mode="None">
        <transport clientCredentialType="Windows" proxyCredentialType="None" realm="" />
        <message clientCredentialType="Windows" negotiateServiceCredential="true" establishSecurityContext="true" />
      </security>
    </binding>
    <binding name="WSHttpServiceMagicBinding" closeTimeout="00:10:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:10:00" bypassProxyOnLocal="true" transactionFlow="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="999524288" maxReceivedMessageSize="655360000" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
      <readerQuotas maxDepth="32" maxStringContentLength="900000" maxArrayLength="900000" maxBytesPerRead="900000" maxNameTableCharCount="900000"/>
      <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false"/>
      <security mode="None">
        <transport clientCredentialType="Windows" proxyCredentialType="None" realm=""/>
        <message clientCredentialType="Windows" negotiateServiceCredential="true" establishSecurityContext="true"/>
      </security>
    </binding>
  </wsHttpBinding>
</bindings>

客户的客户部分:

<client>
    <endpoint address="http://hydwebd02.solutions.com/GeoService.Saveology.com/ServiceMagicService.svc"
        binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IServiceMagicService"
        contract="IServiceMagicService" name="WSHttpBinding_IServiceMagicService" />
</client>

服务器的服务部分:

<services>
    <service behaviorConfiguration="GeoService.Saveology.com.CityStateServiceProviderBehavior"
    name="GeoService.Saveology.com.CityStateServiceProvider">
    <endpoint binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_SEOService"
        contract="SEO.Common.ServiceContract.ICityStateService" />
    <endpoint address="mex" binding="mexHttpBinding" bindingConfiguration=""
        contract="IMetadataExchange" />
    </service>
    <service behaviorConfiguration="GeoService.Saveology.com.ServiceMagicServiceProviderBehavior"
    name="GeoService.Saveology.com.ServiceMagicServiceProvider">
    <endpoint binding="wsHttpBinding" bindingConfiguration="WSHttpServiceMagicBinding" 
        contract="SEO.Common.ServiceContract.IServiceMagicService">
    </endpoint>
    <endpoint address="mex" binding="mexHttpBinding" bindingConfiguration="" contract="IMetadataExchange" />
    </service>
</services>

当您使用真实客户端调用这些方法时会发生什么? - John Saunders
好的,谢谢 - 但你还需要发布服务器的<services>部分和客户端的<client>部分,这样我们才能看到这些端点是如何设置和调用它们的。 - marc_s
使用真实客户端,没有任何问题。 - user82613
我已经发布了客户端的<client>部分和服务器的<services>部分。 - user82613
2
在 .Net 的世界中,这种情况很常见,你会得到很多(有效的)不同答案来回答同一个问题,但没有一个人解释你正在改变哪些参数以及为什么要这样做。有人真正理解他/她正在做什么吗? - Thibault D.
可能是因为密码错误导致的。 - Abhinav Galodha
10个回答

85

我对我要调用的服务的安全配置没有控制权,但仍然遇到了同样的错误。我按照以下步骤修复了我的客户端。

  1. 在配置中设置安全模式:

    <security mode="TransportCredentialOnly">
      <transport clientCredentialType="Windows" proxyCredentialType="None" realm="" />
      <message clientCredentialType="UserName" algorithmSuite="Default" />
    </security>
    
    在代码中,将代理类设置为允许模拟(我添加了一个名为customer的服务的引用):
  2. Customer_PortClient proxy = new Customer_PortClient();
    proxy.ClientCredentials.Windows.AllowedImpersonationLevel =    
             System.Security.Principal.TokenImpersonationLevel.Impersonation;
    

2
如果您要以编程方式执行此操作,请确保使用Dim binding As New System.ServiceModel.BasicHttpBinding()而不是Dim binding As New System.ServiceModel.WSHttpBinding()。 - Stefan Steiger
2
我真的不知道什么是Customer_PortClient,但设置安全模式帮了我大忙。 - Nathan
3
当使用 <security mode="TransportCredentialOnly"> 时,似乎不需要包含有关消息安全性的配置,SOAP 消息以明文形式未加密地发送。 <message> 标记似乎被忽略,因此不会造成任何损害,只是不必要的。来源:https://msdn.microsoft.com/en-us/library/ff648505.aspx - Ben Cottrell
“the config”在哪里?我有点新手。编辑:从这里找到了解决方法:https://learn.microsoft.com/en-us/dotnet/framework/wcf/how-to-set-the-security-mode#to-set-the-mode-and-clientcredentialtype-property-in-configuration - Crhistian Ramirez

13

我有类似的问题,你尝试过:

proxy.ClientCredentials.Windows.AllowedImpersonationLevel =   
          System.Security.Principal.TokenImpersonationLevel.Impersonation;

10

我发现了另一种可能解决此错误的方法。可能没有回答OP的确切问题,但可能会帮助其他遇到此错误信息的人。

我正在使用WebHttpBinding在代码中创建我的客户端,以复制以下行:

<security mode="TransportCredentialOnly">
  <transport clientCredentialType="Windows" proxyCredentialType="Windows" />
</security>

我必须做:

var binding = new WebHttpBinding(WebHttpSecurityMode.TransportCredentialOnly);
                binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
                binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.Windows;

除此之外,还需要设置 proxy.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;


8

我曾遇到类似问题,尝试了以上所有建议。然后我尝试将clientCreditialType更改为Basic,一切都正常工作了。

<basicHttpBinding>
    <binding name="BINDINGNAMEGOESHERE" >
      <security mode="TransportCredentialOnly">
        <transport clientCredentialType="Basic"></transport>
      </security>
    </binding>
  </basicHttpBinding>

7
我看到这个问题还没有得到答案,这是一个来自这里的确切引用:
“WSHttpBinding会尝试在SSP层执行内部协商。为了使其成功,您需要允许IIS中VDir的匿名访问。然后WCF将默认执行SPNEGO以进行窗口凭据认证。在IIS层允许匿名访问并不意味着允许任何人进入,而是转交给了WCF堆栈。”
我通过http://fczaja.blogspot.com/2009/10/http-request-is-unauthorized-with.html找到了这个引用。
在谷歌搜索:http://www.google.tt/#hl=en&source=hp&q=+The+HTTP+request+is+unauthorized+with+client+authentication+scheme+%27Anonymous之后发现。

嗨@Irwin, 感谢您的回答。您能否提供更多“官方”的来源,例如微软制作的原始文档,以便我向审计员证明此事?非常感谢! Marcelo - Marcelo Finki

3
这是让我成功工作所需做的事情。具体包括:
  1. 自定义UserNamePasswordValidator(不需要Windows帐户、SQLServer或ActiveDirectory——你的UserNamePasswordValidator可以将用户名和密码硬编码,或者从文本文件、MySQL或其他位置读取)。
  2. https
  3. IIS7
  4. .net 4.0
我的网站通过DotNetPanel进行管理。它有三个虚拟目录的安全选项:
  1. 允许匿名访问
  2. 启用基本身份验证
  3. 启用集成Windows身份验证
只需要“允许匿名访问”(尽管仅仅这样还不够)。
设置
proxy.ClientCredentials.Windows.AllowedImpersonationLevel =  System.Security.Principal.TokenImpersonationLevel.Impersonation;

在我的情况下,这并没有什么区别。

然而,使用此绑定是有效的:

      <security mode="TransportWithMessageCredential">
        <transport clientCredentialType="Windows" />
        <message clientCredentialType="UserName" />
      </security>        

2
今天我遇到了同样的错误,在将我们的服务部署到Azure的staging环境后调用外部服务时出现了错误。本地服务调用外部服务没有出错,但部署后就出错了。
最终发现是外部服务有一个IP验证机制。Azure中的新环境具有另一个IP地址,因此被拒绝访问。
因此,如果您在调用外部服务时遇到此错误,
它可能是IP限制导致的。

我有同样的问题。请注意,如果Azure应用服务计划不是专用的,则出站IP是虚拟的,将来可能会更改。 - anhtv13

2

我也遇到了这个错误,最终这些代码对我在dot net core 3.1中起作用了。

首先,在命令提示符中安装svcutil:dotnet tool install --global dotnet-svcutil

然后关闭命令提示符并重新打开。

接着,在命令提示符中创建Reference.cs

dotnet-svcutil http://YourService.com/SayHello.svc

(需要输入回车键、用户名和密码)

将名为Connected Services的文件夹添加到项目根目录中。

复制 Reference.cs 文件到 Connected Services 文件夹中。

在创建 BasicHttpBinding 并设置 MaxBufferSize 的行后,将这4行代码添加到 Reference.cs 中:

result.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
result.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
result.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
result.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;

在你的控制器中使用这个服务:

public async Task<string> Get()
    {
        try
        {
            var client = new EstelamClient();
            client.ClientCredentials.UserName.UserName = "YourUserName";
            client.ClientCredentials.UserName.Password = "YourPassword";
            var res = await client.EmployeeCheckAsync("service parameters");
            return res.ToString();
        }
        catch (Exception ex)
        {
            return ex.Message + " ************ stack : " + ex.StackTrace;
        }

    }

不要忘记在 csproj 中安装这些包:

<PackageReference Include="System.ServiceModel.Duplex" Version="4.6.*" />
<PackageReference Include="System.ServiceModel.Http" Version="4.6.*" />
<PackageReference Include="System.ServiceModel.NetTcp" Version="4.6.*" />
<PackageReference Include="System.ServiceModel.Security" Version="4.6.*" />

result 的类型是什么? - Phi
1
这取决于您使用的方法。在我的代码中,EmployeeCheckAsync()返回一个对象,该对象位于Reference.cs文件中,该文件是通过dotnet-svcutil命令创建的。 - M Komaei
这个解决方案对我很有帮助,特别是 "binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic" 这一行。我创建了一个新的绑定,并将其作为参数传递给客户端构造函数。 - Wagner Bertolini Junior

0
尝试在您的客户端中提供以下用户名和密码:

client.ClientCredentials.UserName.UserName = @"Domain\username";
client.ClientCredentials.UserName.Password = "password";

0

我在开发机上遇到了这个问题(生产环境没有问题)。我修改了IIS中的配置,允许匿名访问,并将我的用户名和密码作为凭据。

虽然不是最好的方法,但它适用于测试目的。


请问您能否在这里分享相应的代码片段? - Aman Jain
我会尝试找到它,但这篇帖子是7年前的,所以我不知道能否找到它... - David Brunelle

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