简体   繁体   中英

Authorization Header is missing in Http request using WCF

I am accessing a web service using WCF. Using WSHttpBinding, Security mode is set Transport (https) and client credential type is Basic. When I try to access the service using the proxy, getting an 401 unauthorized exception.

Here is the Binding

var binding = new WSHttpBinding()
        {
            UseDefaultWebProxy = true,
            Security =
            {
                Mode = SecurityMode.Transport,
                Transport =
                {
                    ClientCredentialType = HttpClientCredentialType.Basic,
                },
            }
        };

Here is the service call

var client = new InternetClient(binding, new EndpointAddress("httpsurl"));

        client.ClientCredentials.UserName.UserName = "username";
        client.ClientCredentials.UserName.Password = "password";
        client.ProcessMessage("somevalue");

When looked into Http headers using Http Analyzer CONNECT HEADER

(Request-Line):CONNECT somehost.com:443 HTTP/1.1
Host:somehost.com
Proxy-Connection:Keep-Alive

POST HEADER

(Request-Line):POST /Company/1.0 HTTP/1.1
Content-Type:application/soap+xml; charset=utf-8
VsDebuggerCausalityData:uIDPo+voStemjalOv5LtRotFQ7UAAAAAUKLJpa755k6oRwto14BnuE2PDtYKxr9LhfqXFSOo8pEACQAA
Host:somehost.com
Content-Length:898
Expect:100-continue
Connection:Keep-Alive

If you see the header Authorization header is missing

Now my question is why WCF call missing the Authorization header? Am I missing something? . Please ask if you need more information

This is a common problem, but the situation is different from what you think.

It turns out that initially for the 1st request a WCF client that is configured to use HTTP basic authentication will nevertheless send the request without the necessary Authorization header to the server. This is the default behavior of the HttpWebRequest class used by the WCF client.

Normally, the web service server will then return a HTTP 401 Unauthorized response to the WCF client, upon which the latter will resend the message with the Authorization header. This means under normal conditions for HTTP Basic Authentication there will be aa rather useless round trip to the server.

This also explains why the header was missing in your sniffed message. Some Http sniffs possibly don't pass on the 401 response, so the whole exchange gets messed up.

The server round-trip and dependence on the 401 response can be avoided by manually injecting the required Authorization header into every request. See eg how to manually inject Authorization header into WCF request

As a slight modification from a previous answer, to support async / await calls, you can actually create a new OperationContext and pass it around on whatever thread you like (as long as it is not shared across concurrent threads as it isn't a thread-safe object)

var client = new MyClient();
client.ClientCredentials.UserName.UserName = "username"; 
client.ClientCredentials.UserName.Password = "password";
var httpRequestProperty = new HttpRequestMessageProperty();
httpRequestProperty.Headers[HttpRequestHeader.Authorization] = "Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes(client.ClientCredentials.UserName.UserName + ":" + client.ClientCredentials.UserName.Password));

var context = new OperationContext(ormClient.InnerChannel);
using (new OperationContextScope(context))
{
    context.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = httpRequestProperty;
    return await client.SomeMethod();
}

I had the exact same issues. I was able to manually inject the authorization headers by using the following code:

var callcontext = new CAdxCallContext();
callcontext.codeLang = "ENG";
callcontext.poolAlias = "BGRTEST";

var proxy = new CAdxWebServiceXmlCCClient();
proxy.Endpoint.EndpointBehaviors.Add(new CustomEndpoint()); 
proxy.ClientCredentials.UserName.UserName = "USERNAME"; // Might not benecessary
proxy.ClientCredentials.UserName.Password = "PASSWORD"; // Might not benecessary

string inputXml = "<PARAM>" +
       "<GRP ID= \"GRP1\">" +
       "<FLD NAME = \"ITMREF\">" + "100001" + "</FLD>" +
       "</GRP>" +
       "</PARAM>";

CAdxResultXml response;
try
{
    response = proxy.run(callcontext, "BGR_SIEPRO", inputXml);
}
catch (TimeoutException timeout)
{
    Console.WriteLine(timeout.Message);
    // handle the timeout exception.
    proxy.Abort();
}
catch (CommunicationException commexception)
{
    Console.WriteLine(commexception.Message);
    // handle the communication exception.
    proxy.Abort();
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}
finally
{
     proxy.Close();
}
}

    public class ClientMessageInspector : IClientMessageInspector
    {
        public void AfterReceiveReply(ref Message reply, object correlationState)
        {
            // Nothing Here
            Console.Write(reply.ToString());
        }

        public object BeforeSendRequest(ref Message request, IClientChannel channel)
        {
            HttpRequestMessageProperty httpRequestProperty = new HttpRequestMessageProperty();
            httpRequestProperty.Headers[HttpRequestHeader.Authorization] = "Basic " +
                Convert.ToBase64String(Encoding.ASCII.GetBytes("USERNAME" + ":" +
                    "PASSWORD"));
            request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestProperty);
            return null;
        }
    }

    public class CustomEndpoint : IEndpointBehavior
    {
        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
            // Nothing here
        }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
            clientRuntime.ClientMessageInspectors.Add(new ClientMessageInspector());
        }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            // Nothing here
        }

        public void Validate(ServiceEndpoint endpoint)
        {
            // Nothing here
        }
    }

Notice the Expect:100-continue in the header. That's the reason for the round trip.

Put this in your web.config and try again:

<system.net>
    <settings>
      <servicePointManager expect100Continue="false"/>
    </settings>
</system.net>

Actually, I was wrong about this question. I did see different behaviour when running HTTP analyzer. While Http anaylzer running, my application crashed after receiving 401 response. When Http analyzer application closed, the above code worked as expected.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM