代码之家  ›  专栏  ›  技术社区  ›  Andomar

httpwebrequests在授权头中发送无参数URI

  •  6
  • Andomar  · 技术社区  · 14 年前

    我正在从.NET连接到Web服务,例如:

    var request = (HttpWebRequest) WebRequest.Create(uri);
    request.Credentials = new NetworkCredential("usr", "pwd", "domain");
    var response = (HttpWebResponse) request.GetResponse();
    

    授权头如下所示:

    Authorization: Digest username="usr",realm="domain",nonce="...",
        uri="/dir",algorithm="MD5",etc...
        ^^^^^^^^^^
    

    服务器返回(400)错误请求。通过chrome或ie发送的头文件如下:

    Authorization: Digest username="usr", realm="domain", nonce="...", 
        uri="/dir/query?id=1", algorithm=MD5, etc...
        ^^^^^^^^^^^^^^^^^^^^^
    

    我们怀疑URI中的差异导致Web服务以400错误拒绝请求。是否可以使httpRequest发送包含完整URI的授权头?

    3 回复  |  直到 12 年前
        1
  •  10
  •   Andomar    14 年前

    结果是 Digest authentication 很容易实现。通过我们自己的实现,我们能够使用完整的URI(包括参数)来生成MD5哈希。解决了问题。

    如果将来有人遇到此问题,您可以调用如下解决方法:

    var resultText = DigestAuthFixer.GrabResponse("/dir/index.html");
    

    DigestAuthFixer类的代码:

    public static class DigestAuthFixer
    {
        private static string _host = "http://localhost";
        private static string _user = "Mufasa";
        private static string _password = "Circle Of Life";
        private static string _realm;
        private static string _nonce;
        private static string _qop;
        private static string _cnonce;
        private static DateTime _cnonceDate;
        private static int _nc;
    
        private static 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 static 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;
            throw new ApplicationException(string.Format("Header {0} not found", varName));
        }
    
        // http://en.wikipedia.org/wiki/Digest_access_authentication
        private static string GetDigestHeader(
            string dir)
        {
            _nc = _nc + 1;
    
            var ha1 = CalculateMd5Hash(string.Format("{0}:{1}:{2}", _user, _realm, _password));
            var ha2 = CalculateMd5Hash(string.Format("{0}:{1}", "GET", dir));
            var digestResponse =
                CalculateMd5Hash(string.Format("{0}:{1}:{2:00000000}:{3}:{4}:{5}", ha1, _nonce, _nc, _cnonce, _qop, ha2));
    
            return string.Format("Digest username=\"{0}\", realm=\"{1}\", nonce=\"{2}\", uri=\"{3}\", " +
                "algorithm=MD5, response=\"{4}\", qop={5}, nc={6:00000000}, cnonce=\"{7}\"",
                _user, _realm, _nonce, dir, digestResponse, _qop, _nc, _cnonce);
        }
    
        public static string GrabResponse(
            string dir)
        {
            var url = _host + dir;
            var uri = new Uri(url);
    
            var request = (HttpWebRequest)WebRequest.Create(uri);
    
            // If we've got a recent Auth header, re-use it!
            if (!string.IsNullOrEmpty(_cnonce) &&
                DateTime.Now.Subtract(_cnonceDate).TotalHours < 1.0)
            {
                request.Headers.Add("Authorization", GetDigestHeader(dir));
            }
    
            HttpWebResponse response;
            try
            {
                response = (HttpWebResponse)request.GetResponse();
            }
            catch (WebException ex)
            {
                // Try to fix a 401 exception by adding a Authorization header
                if (ex.Response == null || ((HttpWebResponse)ex.Response).StatusCode != HttpStatusCode.Unauthorized)
                    throw;
    
                var wwwAuthenticateHeader = ex.Response.Headers["WWW-Authenticate"];
                _realm = GrabHeaderVar("realm", wwwAuthenticateHeader);
                _nonce = GrabHeaderVar("nonce", wwwAuthenticateHeader);
                _qop = GrabHeaderVar("qop", wwwAuthenticateHeader);
    
                _nc = 0;
                _cnonce = new Random().Next(123400, 9999999).ToString();
                _cnonceDate = DateTime.Now;
    
                var request2 = (HttpWebRequest)WebRequest.Create(uri);
                request2.Headers.Add("Authorization", GetDigestHeader(dir));
                response = (HttpWebResponse)request2.GetResponse();
            }
            var reader = new StreamReader(response.GetResponseStream());
            return reader.ReadToEnd();
        }
    }
    
        2
  •  5
  •   Gerfboy    12 年前

    我最近碰到这个问题。如果没有一些小的调整,我也无法从Andomar获得解决方案。我把这些修改作为对安多马尔答案的建议,但他们被提曼和路西法毫不客气地拒绝了。因为我和同事们花了好几个小时才弄明白这些问题,而且我确信其他人也会需要这些问题,所以我发布了代码作为答案,以使其可用。

    这是调整后的代码。基本上,需要一个“不透明”的头变量,一些引号需要在getDigestHeader中修复。

    public static class DigestAuthFixer
    {
        private static string _host = "http://localhost";
        private static string _user = "Mufasa";
        private static string _password = "Circle Of Life";
        private static string _realm;
        private static string _nonce;
        private static string _qop;
        private static string _cnonce;
        private static string _opaque;
        private static DateTime _cnonceDate;
        private static int _nc = 0;
    
        private static 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 static 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;
            throw new ApplicationException(string.Format("Header {0} not found", varName));
        }
    
        // http://en.wikipedia.org/wiki/Digest_access_authentication
        private static string GetDigestHeader(
            string dir)
        {
            _nc = _nc + 1;
    
            var ha1 = CalculateMd5Hash(string.Format("{0}:{1}:{2}", _user, _realm, _password));
            var ha2 = CalculateMd5Hash(string.Format("{0}:{1}", "GET", dir));
            var digestResponse =
                CalculateMd5Hash(string.Format("{0}:{1}:{2:00000000}:{3}:{4}:{5}", ha1, _nonce, _nc, _cnonce, _qop, ha2));
    
            return string.Format("Digest username=\"{0}\", realm=\"{1}\", nonce=\"{2}\", uri=\"{3}\", " +
            "algorithm=MD5, response=\"{4}\", qop=\"{5}\", nc=\"{6:00000000}\", cnonce=\"{7}\", opaque=\"{8}\"",
            _user, _realm, _nonce, dir, digestResponse, _qop, _nc, _cnonce, _opaque);
        }
    
        public static string GrabResponse(
            string dir)
        {
            var url = _host + dir;
            var uri = new Uri(url);
    
            var request = (HttpWebRequest)WebRequest.Create(uri);
    
            // If we've got a recent Auth header, re-use it!
            if (!string.IsNullOrEmpty(_cnonce) &&
                DateTime.Now.Subtract(_cnonceDate).TotalHours < 1.0)
            {
                request.Headers.Add("Authorization", GetDigestHeader(dir));
            }
    
            HttpWebResponse response;
            try
            {
                response = (HttpWebResponse)request.GetResponse();
            }
            catch (WebException ex)
            {
                // Try to fix a 401 exception by adding a Authorization header
                if (ex.Response == null || ((HttpWebResponse)ex.Response).StatusCode != HttpStatusCode.Unauthorized)
                    throw;
    
                var wwwAuthenticateHeader = ex.Response.Headers["WWW-Authenticate"];
                _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;
    
                var request2 = (HttpWebRequest)WebRequest.Create(uri);
                request2.Headers.Add("Authorization", GetDigestHeader(dir));
                response = (HttpWebResponse)request2.GetResponse();
            }
            var reader = new StreamReader(response.GetResponseStream());
            return reader.ReadToEnd();
        }
    }
    
        3
  •  0
  •   Samiksha    14 年前

    看起来您需要安装此修补程序可能会帮助您:

    http://support.microsoft.com/?kbid=924638

    您的问题可能是因为在使用HTTP适配器发布消息时无法将Keepalive属性设置为false。

    同时确保preauthenticate设置为true。