简体   繁体   中英

RestSharp Digest Authenticator is not working

I'm sending a POST request using RestSharp in C# which requires digest authentication (using package RestSharp.Authenticators.Digest). When I use client.Authenticator = new DigestAuthenticator("username", "password"); the client.Execute(request); method throws an exception

Header not found: qop at RestSharp.Authenticators.Digest.DictionaryHeaderExtension.GetHeader(IDictionary 2 header, String key) at RestSharp.Authenticators.Digest.DigestAuthenticatorManager.GetDigestDataFromException(WebException ex) at RestSharp.Authenticators.Digest.DigestAuthenticatorManager.GetDigestAuthHeader(String path, Method method) at RestSharp.Authenticators.Digest.DigestAuthenticator.Authenticate(IRestClient client, IRestRequest request) at RestSharp.RestClient.AuthenticateIfNeeded(IRestRequest request) at RestSharp.RestClient.Execute(IRestRequest request, String httpMethod, Func`3 getResponse) at RestSharp.RestClient.Execute(IRestRequest request)**

Use this authenticator instead. It won't work with everything but it is much faster and does not use any extra nuggets. I don't have much experience with Digest but this worked for me. I may fail if you need more than one cookie or if the digest uses extra parameters.

public class RESTDigestAuthenticator : IAuthenticator
{
    readonly string _username;
    readonly string _password;

    private static string _realm;
    private static string _nonce;
    private static string _qop;
    private static string _opaque;
    private static string _cnonce;
    private static DateTime _cnonceDate;
    private static int _nc;

    public RESTDigestAuthenticator(string username, string password)
    {
        _username = username;
        _password = password;
    }

    void IAuthenticator.Authenticate(IRestClient client, IRestRequest request)
    {
        //Debug.WriteLine($"base={client.BaseUrl}, resource={request.Resource}");           

        // If we've got a recent Auth header, re-use it!
        if (!string.IsNullOrEmpty(_cnonce) &&
            DateTime.Now.Subtract(_cnonceDate).TotalHours < 1.0)
        {
            request.AddHeader("Authorization", GetDigestHeader(request.Method.ToString(), request.Resource));
        }
        else
        {
            try
            {

                var response = new RestClient(client.BaseUrl).Execute(request);
                // Try to fix a 401 exception by adding a Authorization header
                if (response == null)
                {
                    throw new Exception($"No response from {client.BaseUrl}");
                }
                else if (response.StatusCode != System.Net.HttpStatusCode.Unauthorized) 
                {
                    throw new Exception($"Expected Unauthorized, got {response.StatusCode}");
                }

                string wwwAuthenticateHeader;
                var wwwHead = response.Headers.FirstOrDefault(x => x.Name == "WWW-Authenticate");
                if (wwwHead != null)
                {
                    wwwAuthenticateHeader = wwwHead.Value.ToString();
                }
                else
                {
                    throw new Exception("no wwwAuthHeader");
                }

                string cookie = null;
                string cookieName = null;
                string cookieVal = null;
                var cook = response.Headers.FirstOrDefault(x => x.Name == "Set-Cookie");
                if (cook != null)
                {
                    cookie = cook.Value.ToString();
                    if (cookie.Contains(";"))
                    {
                        cookie = cookie.Split(new[] { ";" }, StringSplitOptions.None)[0];
                        if (cookie.Contains("="))
                        {
                            var kvp = cookie.Split(new[] { "=" }, StringSplitOptions.None);
                            cookieName = kvp[0];
                            cookieVal = kvp[1];
                        }
                    }
                }
                else
                {
                    Debug.WriteLine("No Set-Cookie", Logger.LogLevel.Trace);
                }

                _realm = GrabHeaderVar("realm", wwwAuthenticateHeader);
                _nonce = GrabHeaderVar("nonce", wwwAuthenticateHeader);
                _qop = GrabHeaderVar("qop", wwwAuthenticateHeader);
                _opaque = GrabHeaderVar("opaque", wwwAuthenticateHeader);                

                _nc = 0;
                _cnonce = new Random().Next(123400, 9999999).ToString();
                _cnonceDate = DateTime.Now;
                request.AddHeader("Authorization", GetDigestHeader(request.Method.ToString(), request.Resource));

                if (!string.IsNullOrWhiteSpace(cookieName) && !string.IsNullOrWhiteSpace(cookieVal))
                {
                    request.AddCookie(cookieName, cookieVal);
                }
                //request.Parameters.RemoveAll(x => x.Value.ToString().Contains("gzip, deflate"));
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
            }
        }
    }

    private string CalculateMd5Hash(
        string input)
    {
        var inputBytes = Encoding.ASCII.GetBytes(input);
        var hash = MD5.Create().ComputeHash(inputBytes);
        var sb = new StringBuilder();
        foreach (var b in hash)
            sb.Append(b.ToString("x2"));
        return sb.ToString();
    }

    private string GrabHeaderVar(
        string varName,
        string header)
    {
        var regHeader = new Regex(string.Format(@"{0}=""([^""]*)""", varName));
        var matchHeader = regHeader.Match(header);
        if (matchHeader.Success)
        {
            return matchHeader.Groups[1].Value;
        }
        else
        {
            Debug.WriteLine($"Header {varName} not found");
            return null;
        }           
    }

    private string GetDigestHeader(string method, string dir)
    {
        if (!dir.StartsWith("/"))
        {
            dir = "/" + dir;
        }
        _nc = _nc + 1;

        var ha1 = CalculateMd5Hash(string.Format("{0}:{1}:{2}", _username, _realm, _password));
        var ha2 = CalculateMd5Hash(string.Format("{0}:{1}", method.ToUpper(), dir));
        var digestResponse =
            CalculateMd5Hash(string.Format("{0}:{1}:{2:00000000}:{3}:{4}:{5}", ha1, _nonce, _nc, _cnonce, _qop, ha2));

        string header = string.Format($"Digest username=\"{_username}\",realm=\"{_realm}\",nonce=\"{_nonce}\",uri=\"{dir}\"," + //removed spaces
            $"algorithm=MD5,response=\"{digestResponse}\",qop={_qop},nc={_nc:00000000},cnonce=\"{_cnonce}\"");
        if (!string.IsNullOrWhiteSpace(_opaque))
        {
            header += $",opaque=\"{_opaque}\"";
        }
        return header;
    }


}

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