Double request while making POX REST call using WC

2019-02-19 06:50发布

Having an issue while making POX REST call using WCF with WebHttpBinding set to Basic Authentication (HttpClientCredentialType.Basic)

Instead of one call from the client with “Authorization: Basic” specified in HTTP Header, two calls are made. First call without authentication at all to which service responses with 401 Unauthorized error, second call with proper authentication info.

This seems to be handled by WCF service with no hiccup at all. Calling third party services obviously creates a problem since they respond with error immediately.

Service Code:

[ServiceContract]
public interface IService
{
    [OperationContract]
    [WebInvoke(BodyStyle = WebMessageBodyStyle.Bare,
        RequestFormat = WebMessageFormat.Xml,
        UriTemplate = "")]
    Message SendData(Message message);

}

public class Service : IService
{
    public Message SendData(Message message)
    {           return Message.CreateMessage(MessageVersion.None, String.Empty, "test");
    }
}

Client Code:

public class Client: WebChannelFactory<IService>, IService
{
    public Client(Uri baseUri, string userName, string password)
        : base(CreateBinding(),
               baseUri)
    {
        Credentials.UserName.UserName = userName;
        Credentials.UserName.Password = password;
    }

    public Message SendData(Message requestMessage)
    {
        var channel = CreateChannel();
        Message responseMessage = channel.SendData(requestMessage);
        return responseMessage;
    }

    private static Binding CreateBinding()
    {
        var binding = new WebHttpBinding();
        binding.Security.Mode = WebHttpSecurityMode.TransportCredentialOnly;
        binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
        return binding;
    }

}

Using TcpTrace I’m seeing these to requests back to back:

POST / HTTP/1.1  
Content-Type: application/xml; charset=utf-8  
VsDebuggerCausalityData:   uIDPo2lH6p+lUOdFmrqDKGWYeQkAAAAA7+Y4QR6wNUWZmwCaasMx7xrfcJZxph9NocstwCh8NQsACQAA  
Host: localhost:9090  
Content-Length: 89  
Expect: 100-continue  
Connection: Keep-Alive  

<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">test request</string>  

POST / HTTP/1.1  
Content-Type: application/xml; charset=utf-8  
VsDebuggerCausalityData: uIDPo2lH6p+lUOdFmrqDKGWYeQkAAAAA7+Y4QR6wNUWZmwCaasMx7xrfcJZxph9NocstwCh8NQsACQAA  
Authorization: Basic dGVzdDp0ZXN0  
Host: localhost:9090  
Content-Length: 89  
Expect: 100-continue  

<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">test request</string> 

Note only second call contains: Authorization: Basic dGVzdDp0ZXN0 How to stop the first request (without authorization) to be made?

Sample solution with TcpTrace utility can be downloaded here:

WCF-BasicAuthenticationIssue.zip

2条回答
放荡不羁爱自由
2楼-- · 2019-02-19 07:11

The problem actually lies with those third parties. Per RFC 2617 Basic authentication is done in two calls, just like Digest. The server should respond at the first call with a challenge containing a realm:

Upon receipt of an unauthorized request for a URI within the
protection space, the origin server MAY respond with a challenge like
the following:

  WWW-Authenticate: Basic realm="WallyWorld"

where "WallyWorld" is the string assigned by the server to identify
the protection space of the Request-URI

The WCF endpoint will pre-authenticate only subsequent calls after the first one. The very first call made by an appdomain to any resource will not contain the basic header user name and password.

See also What happened to the PreAuthenticate flag in WCF.

查看更多
别忘想泡老子
3楼-- · 2019-02-19 07:21

So based on Remus's answer this is my workaround

        public Message SendData(Message requestMessage)
    {
        var channel = CreateChannel();
        Message responseMessage;
        using (new OperationContextScope((IClientChannel)channel))
        {
            WebOperationContext.Current.OutgoingRequest
                .Headers[HttpRequestHeader.Authorization] = "Basic "
                + Convert.ToBase64String(Encoding.ASCII.GetBytes(
                Credentials.UserName.UserName + ":" + Credentials.UserName.Password));
            responseMessage = channel.SendData(requestMessage); 
        }
        return responseMessage;
    }

I'm simply forcing first request to go out with Basic Authorization

查看更多
登录 后发表回答