简体   繁体   中英

Royal Mail Shipping API v2 SOAP authentication C#

I've done most of the work based on other people's code on stack overflow. See below.

My current issue is that I'm still getting authorization failure and I can clearly see why. v2 API requires X-IBM-Client-Id and X-IBM-Client-Secret to be passed as well as SOAP security header. However, I don't know how to inject it while using service created from Wsdl file.

Solved problems:

Overcame namespaces problem (using message formatter Consume WCF Royal Mail API in c# Console Application ).

Solved binding configuration problem which results in two security headers. Also, you must set maxReceivedMessageSize if you want to get no exception while retrieving label.Final binding:

<system.serviceModel> <bindings> <basicHttpBinding> <binding name="basicBindingTransport" maxReceivedMessageSize="2147483647"> <security mode="Transport"> </security> </binding> </basicHttpBinding> </bindings> <client> <endpoint address="https://api.royalmail.net/shipping/v2" binding="basicHttpBinding" bindingConfiguration="basicBindingTransport" contract="RoyalMailApiWsdl.shippingAPIPortType" name="shippingAPIPort" /> </client> </system.serviceModel>

Solved E0007 Authorization Failure.

Solved The HTTP request is unauthorized with client authentication scheme 'Anonymous' (you must use the binding as above with security "Transport" and inject credentials directly into http post header itself (See my answer below).

And many other issues, which I cannot remember now. I hope this post will help others.

To implement double authentification in the Shipping API v2, you can use this code (the idea taken from How to add custom Http Header for C# Web Service Client consuming Axis 1.4 Web service )

shippingAPIPortTypeClient client = GetProxy();
<..>
using (OperationContextScope scope = new OperationContextScope(client.InnerChannel))
{
    var httpRequestProperty = new HttpRequestMessageProperty();
    httpRequestProperty.Headers.Add(@"X-IBM-Client-Id", _credentials.HttpSecurity.ClientId);
    httpRequestProperty.Headers.Add(@"X-IBM-Client-Secret", _credentials.HttpSecurity.ClientSecret);
    OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = httpRequestProperty;

    createShipmentResponse response = client.createShipment(GetSecurityHeaderType(), request);
    return response;
}

private shippingAPIPortTypeClient GetProxy()
{
    // binding comes from configuration file
    var shippingClient = new shippingAPIPortTypeClient();

    shippingClient.ClientCredentials.UserName.UserName = _credentials.SoapSecurity.Username;
    shippingClient.ClientCredentials.UserName.Password = _credentials.SoapSecurity.Password;

    shippingClient.ClientCredentials.UseIdentityConfiguration = true;

    foreach (OperationDescription od in shippingClient.Endpoint.Contract.Operations)
    {
        od.Behaviors.Add(new RoyalMailIEndpointBehavior());
    }

    return shippingClient;
}

private SecurityHeaderType GetSecurityHeaderType()
{
    SecurityHeaderType securityHeader = new SecurityHeaderType();

    DateTime created = DateTime.Now;

    string creationDate;
    creationDate = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ");

    string nonce = nonce = (new Random().Next(0, int.MaxValue)).ToString();

    byte[] hashedPassword;
    hashedPassword = GetSHA1(_credentials.SoapSecurity.Password);

    string concatednatedDigestInput = string.Concat(nonce, creationDate, Encoding.Default.GetString(hashedPassword));
    byte[] digest;
    digest = GetSHA1(concatednatedDigestInput);

    string passwordDigest;
    passwordDigest = Convert.ToBase64String(digest);

    string encodedNonce;
    encodedNonce = Convert.ToBase64String(Encoding.Default.GetBytes(nonce));

    XmlDocument doc = new XmlDocument();
    using (XmlWriter writer = doc.CreateNavigator().AppendChild())
    {
        writer.WriteStartDocument();
        writer.WriteStartElement("Security");
        writer.WriteStartElement("UsernameToken", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
        writer.WriteElementString("Username", _credentials.SoapSecurity.Username);
        writer.WriteElementString("Password", passwordDigest);
        writer.WriteElementString("Nonce", encodedNonce);
        writer.WriteElementString("Created", creationDate);
        writer.WriteEndElement();
        writer.WriteEndElement();
        writer.WriteEndDocument();
        writer.Flush();
    }

    doc.DocumentElement.RemoveAllAttributes();

    System.Xml.XmlElement[] headers = doc.DocumentElement.ChildNodes.Cast<XmlElement>().ToArray<XmlElement>();

    securityHeader.Any = headers;

    return securityHeader;

}

On top of that complete working Royal Mail Shipping API v2 solution in C# is available here: https://github.com/povilaspanavas/RoyalMailShippingApiV2

It has three tests, one to create local (GB) shipment, another international, and the third to retrieve label pdf file.

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