请改用此身份验证器。
它不适用于所有东西,但速度更快,并且不使用任何额外的金块。
我对 Digest 没有太多经验,但这对我有用。
如果您需要多个 cookie 或摘要使用额外参数,我可能会失败。
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;
}
}