使用HttpClient.GetAsync()使用具有基本身份验证的WCF REST服务会导致(401)未经授权

时间:2019-02-11 18:50:12

标签: c# .net wcf basic-authentication dotnet-httpclient

我正在尝试使用基本身份验证通过HttpClient连接到WCF自托管的REST服务,但仍要未经授权(401)。当我从Web浏览器访问相同的端点时,输入相同的用户名和密码将成功。服务端的验证是通过UserNamePasswordValidator完成的。为了测试目的,我将Validate方法保留为空,因此所有请求都应有效。但是,调用GetAsync()会导致(401)未经授权。在Validate方法中设置断点时,可以检查是否传递了正确的值。对此行为有何解释?

客户

using (var httpClient = new HttpClient())
{
    var authString = "admin:admin";
    var authEncoded = Encoding.GetEncoding("ISO-8859-1").GetBytes(authString);
    var authBase64String = Convert.ToBase64String(authEncoded);
    httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authBase64String);
    httpClient.BaseAddress = new Uri(UriFactory.GetServiceUrl());

    using (var response = await httpClient.GetAsync(serviceDomain))
    {
        string responseData = await response.Content.ReadAsStringAsync();
        return JsonConverter.FromJson<TResponse>(responseData);
    }
}

服务

public class CustomUserNameValidator : UserNamePasswordValidator
{
    public override void Validate(string userName, string password)
    {
    }
}

这是服务配置

<system.serviceModel>
<bindings>  
  <webHttpBinding>  
    <binding name="HttpsBinding">  
      <security mode="Transport">  
        <transport clientCredentialType="Basic" />  
      </security>  
    </binding>  
  </webHttpBinding>  
</bindings>  

<services>
  <service behaviorConfiguration="MyServiceBeahvior" name="ServiceImplementation">
    <endpoint address="status"        binding="webHttpBinding" bindingConfiguration="HttpsBinding" contract="Status.IStatusService"            behaviorConfiguration="MyWebBahviorr"/>
    <endpoint address="mex"           binding="mexHttpsBinding" contract="IMetadataExchange" />
    <host>
      <baseAddresses />
    </host>
  </service>
</services>

<behaviors>
  <serviceBehaviors>
    <behavior name="MyServiceBeahvior">
      <serviceMetadata httpsGetEnabled="True" />
      <serviceDebug includeExceptionDetailInFaults="True" />
    </behavior>
  </serviceBehaviors>
  <endpointBehaviors>
    <behavior name="MyWebBahvior">
      <webHttp automaticFormatSelectionEnabled="false" />
    </behavior>
  </endpointBehaviors>
</behaviors>

_oServiceHost.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
_oServiceHost.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new CustomUserNameValidator();

通信通过带有自签名证书的HTTPS。

2 个答案:

答案 0 :(得分:1)

最后,我能够使用Fiddler找到我的问题。我发送的请求没有最后的斜杠(例如,https://localhost:4444/status而不是https://localhost:4444/status/)。 Web浏览器能够处理重定向,但是HttpClient失败。

答案 1 :(得分:0)

首先必须注意, CustomUsernamePasswordValidator 仅在客户端凭据为 UserName 时可用。

    <bindings>
      <wsHttpBinding>
        <binding name="wsbd">
          <security mode="Message">
            <message clientCredentialType="UserName"/>
          </security>
        </binding>
      </wsHttpBinding>
</bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="svbhv">
          <serviceAuthorization principalPermissionMode="Custom">
            <authorizationPolicies>
              <add policyType="Server7.CustAuthorPolicy,Server7"/>
            </authorizationPolicies>
          </serviceAuthorization>
          <serviceCredentials>
            <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="Server7.MyCustUserNamePassValidator,Server7"/>
          </serviceCredentials>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>

因此,默认凭据应为服务器Windows凭据。默认情况下,有一个针对自签名证书的验证过程,因此您需要向服务器证书中添加验证回调

ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;

我制作了一个演示,希望它对您有用。 服务器端。

    public interface IService1
    {
        [OperationContract]
        [WebGet]
        string GetData(int value);

}
    public class Service1 : IService1
    {
        public string GetData(int value)
        {
            return string.Format("You entered: {0}", value);
        }
}

配置。

<system.serviceModel>
    <services>
      <service name="WcfService3.Service1">
        <endpoint address="" binding="webHttpBinding" contract="WcfService3.IService1" bindingConfiguration="mybinding" behaviorConfiguration="rest"></endpoint>
        <endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange"></endpoint>
      </service>
    </services>
    <bindings>
      <webHttpBinding>
        <binding name="mybinding">
          <security mode="Transport">
            <transport clientCredentialType="Basic"></transport>
          </security>
        </binding>
      </webHttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpsGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="rest">
          <webHttp/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <protocolMapping>
        <add binding="basicHttpsBinding" scheme="https" />
    </protocolMapping>    
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>

客户端。

  ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
    using (var httpClient = new HttpClient())
    {

        var authString = "administrator:abcd1234!";
        var authEncoded = Encoding.UTF8.GetBytes(authString);
        var authBase64String = Convert.ToBase64String(authEncoded);
        httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",authBase64String);

        var response = httpClient.GetAsync("https://127.0.0.1:8863/Service1.svc/getdata?value=100");
        string responsedata = response.Result.Content.ReadAsStringAsync().Result;
        XmlDocument doc = new XmlDocument();
        doc.LoadXml(responsedata);
        Console.WriteLine(doc.InnerText);
    }

结果。
enter image description here