从WCF客户端调用需要基本HTTP身份验证的Web服务

22

我有一个来自网络服务的WSDL,我生成了WCF代理。没有问题。

但是我不知道如何传递用户名和密码。该网络服务需要基本身份验证,只需要用户名和密码。

有什么帮助吗?

5个回答

28

配置文件中是否配置了基本身份验证?您只需要传递凭据还是还需要安全传输(HTTPS)?

首先,您需要设置绑定以支持基本身份验证。

HTTP绑定设置:

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

设置 HTTPS 绑定:

<bindings>
  <basicHttpBinding>
    <binding name="BasicAuthSecured">
      <security mode="Transport">
        <transport clientCredentialType="Basic" />
      </security>
    </binding>
  </basicHttpBinding>
</bindings>

客户端终端点必须使用定义的配置,例如:

<client>
  <endpoint address="..." 
            name="..." 
            binding="basicHttpBinding" 
            bindingConfiguration="BasicAuth" 
            contract="..."  />
</client>

然后您需要将凭据传递给代理:

proxy = new MyServiceClient();
proxy.ClientCredentials.UserName.UserName = "...";
proxy.ClientCredentials.UserName.Password = "...";

6

对于以下两种情况的人群想了解 .NET Core 项目的相关信息,并对代码变化感兴趣而不是 XML 文件:

  1. 使用dotnet-svcutil来使用 WSDL 框架代码。
  2. Reference.cs方法中更新GetBindingForEndpoint以启用WCF 客户端的基本身份验证
  3. 在使用客户端实例时设置登录名和密码。

示例代码:

private static System.ServiceModel.Channels.Binding GetBindingForEndpoint(EndpointConfiguration endpointConfiguration)
{
    if ((endpointConfiguration == EndpointConfiguration.YourService))
    {
        System.ServiceModel.BasicHttpBinding result = new System.ServiceModel.BasicHttpBinding();
        result.MaxBufferSize = int.MaxValue;
        result.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
        result.MaxReceivedMessageSize = int.MaxValue;
        result.AllowCookies = true;

        // Set Basic Authentication with HTTP protocol (for HTTPS you need "Transport"):
        result.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
        result.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;

        return result;
    }
    throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration));
}

var client = new YourServiceClient();
client.ClientCredentials.UserName.UserName = "yourservicelogin";
client.ClientCredentials.UserName.Password = "yourservicepassword";

我已根据你在这里展示的方式修改了我的GetBindingForEndpoint方法,添加了我的ClientCredentials用户名和密码,使用Fiddler检查请求后发现认证不是我的头部的一部分。有任何想法吗?我正在使用HTTPS,因此我将模式设置为传输模式。 - Antonio Rodríguez
@Antonio,我认为(但我不确定),在Basic Authentication的上下文中,http和https之间没有区别。您必须寻找头部未填充的原因。没有您的代码-我一无所知,很抱歉。 - Gerard Jaryczewski
顺便问一下,你读过这个吗?https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/transport-security-with-basic-authentication - Gerard Jaryczewski
1
我已经检查过了。对于我来说,使用OperationContextScope注入标头是唯一有效的方法。 - Antonio Rodríguez

5

参考 .NET Core 项目中 @Gerard Jaryczewski 的回答,您也可以使用以下扩展方法来编辑 Reference.cs 文件。因为每次更新 Reference.cs 后更改都会被覆盖,所以编辑 Reference.cs 可能很具有挑战性。

public static class BasicAuthenticationExtension
{
    public static void SetBasicAuthentication<T>(this ClientBase<T> client, string userName, string password) where T : class
    {
        if (client == null) throw new ArgumentNullException(nameof(client));
        if (client.Endpoint == null || client.Endpoint.Binding == null) throw new Exception("The specified client has no binding defined!");

        if (client.Endpoint.Binding is BasicHttpsBinding httpsBinding)
        {
            httpsBinding.Security.Mode = BasicHttpsSecurityMode.Transport;
            httpsBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
        }
        else if (client.Endpoint.Binding is BasicHttpBinding httpBinding)
        {
            httpBinding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
            httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
        }
        else
        {
            throw new NotSupportedException("The specified client has a binding defined which is not supporting HTTP basic authentication!");
        }

        client.ClientCredentials.UserName.UserName = userName;
        client.ClientCredentials.UserName.Password = password;
    }
}

然后您可以像这样使用它:

var client = new MyServiceClient();
client.SetBasicAuthentication("myUserName", "myPassword");

-1
我认为这取决于Web服务希望你如何传递信息。毕竟,你只是消费者。
话虽如此,在Web服务中,将用户ID和密码传递到SOAP标头中是很常见的。
你可以参考link中的示例实现此场景。
样例Soap消息。
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header>
    <AUTHHEADER xmlns="http://tempuri.org/">
      <USERNAME>string</USERNAME>
      <PASSWORD>string</PASSWORD>
    </AUTHHEADER>
  </soap:Header>
  <soap:Body>
    <SENSITIVEDATA xmlns="http://tempuri.org/" />
  </soap:Body>
</soap:Envelope>

-1

1
这个问题特别涉及到(HTTP)基本认证。你提供的参考链接只展示了WSHttpBinding。WSHttpBinding在SOAP信封头部的auth块中使用用户名+密码凭据,这与(HTTP)基本认证不是同一回事。 - AlwaysLearning

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