简体   繁体   English

如何使用 HttpWebRequest 进行摘要式身份验证?

[英]How can I do digest authentication with HttpWebRequest?

Various articles ( 1 , 2 ) I discovered make this look easy enough:我发现各种文章( 12 )使这看起来很简单:

WebRequest request = HttpWebRequest.Create(url);

var credentialCache = new CredentialCache();
credentialCache.Add(
  new Uri(url), // request url
  "Digest", // authentication type
  new NetworkCredential("user", "password") // credentials
);

request.Credentials = credentialCache;

However, this only works for URLs without URL parameters.但是,这仅适用于没有 URL 参数的 URL。 For example, I can download http://example.com/test/xyz.html just fine, but when I attempt to download http://example.com/test?page=xyz , the result is a 400 Bad Request message with the following in the server's logs (running Apache 2.2):例如,我可以下载http://example.com/test/xyz.html就好了,但是当我尝试下载http://example.com/test?page=xyz时,结果是一条 400 Bad Request 消息在服务器日志中包含以下内容(运行 Apache 2.2):

Digest: uri mismatch - </test> does not match request-uri </test?page=xyz>

My first idea was that the digest specification requires URL parameters to be removed from the digest hash -- but removing the parameter from the URL passed to credentialCache.Add() didn't change a thing.我的第一个想法是摘要规范要求从摘要散列中删除 URL 参数——但是从传递给credentialCache.Add()的 URL 中删除参数并没有改变任何事情。 So it must be the other way around and somewhere in the .NET framework is wrongly removing the parameter from the URL.所以它一定是相反的,.NET 框架中的某个地方错误地从 URL 中删除了参数。

You said you removed the querystring paramters, but did you try going all the way back to just the host? 你说你删除了查询字符串参数,但你是否尝试一直回到主机? Every single example of CredentialsCache.Add() I've seen seems to use only the host, and the docs for CredentialsCache.Add() list the Uri parameter as "uriPrefix", which seems telling. 我见过的每个CredentialsCache.Add()的例子似乎只使用主机,而CredentialsCache.Add()的文档将Uri参数列为“uriPrefix”,这似乎在说明。

In other words, try this out: 换句话说,试试这个:

Uri uri = new Uri(url);
WebRequest request = WebRequest.Create(uri);

var credentialCache = new CredentialCache(); 
credentialCache.Add( 
  new Uri(uri.GetLeftPart(UriPartial.Authority)), // request url's host
  "Digest",  // authentication type 
  new NetworkCredential("user", "password") // credentials 
); 

request.Credentials = credentialCache;

If this works, you will also have to make sure that you don't add the same "authority" to the cache more than once... all requests to the same host should be able to make use of the same credential cache entry. 如果这样做,您还必须确保不会多次向缓存添加相同的“权限”...对同一主机的所有请求都应该能够使用相同的凭据缓存条目。

Code taken from this post has worked perfectly for me Implement Digest authentication via HttpWebRequest in C# 从这篇文章中获取的代码非常适合我在C#中通过HttpWebRequest实现摘要式身份验证

I had following issue, when ever I browser the feed url in a browser it asked for username and password and worked fine, however any of the above code samples were not working, on inspecting Request/Response Header (in web developer tools in firefox) i could see header having Authorization of type digest. 我有以下问题,当我在浏览器中浏览源URL时它要求用户名和密码并且工作正常,但是上面的任何代码示例都没有工作,检查请求/响应头(在Firefox中的Web开发人员工具中)我可以看到具有类型摘要授权的标题。

Step 1 Add: 第1步添加:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using System.Net;
using System.IO;

namespace NUI
{
    public class DigestAuthFixer
    {
        private static string _host;
        private static string _user;
        private static string _password;
        private static string _realm;
        private static string _nonce;
        private static string _qop;
        private static string _cnonce;
        private static DateTime _cnonceDate;
        private static int _nc;

    public DigestAuthFixer(string host, string user, string password)
    {
        // TODO: Complete member initialization
        _host = host;
        _user = user;
        _password = password;
    }

    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;
        throw new ApplicationException(string.Format("Header {0} not found", varName));
    }

    private 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 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();
    }
}

} }

Step 2: Call new method 第2步:调用新方法

DigestAuthFixer digest = new DigestAuthFixer(domain, username, password);
string strReturn = digest.GrabResponse(dir);

if Url is: http://xyz.rss.com/folder/rss then domain: http://xyz.rss.com (domain part) dir: /folder/rss (rest of the url) 如果网址是: http//xyz.rss.com/folder/rss然后域名: http//xyz.rss.com (域名部分)目录:/文件夹/ rss(网址的其余部分)

you could also return it as stream and use XmlDocument Load() method. 您也可以将其作为流返回并使用XmlDocument Load()方法。

The solution is to activate this parameter in apache: 解决方案是在apache中激活此参数:

    BrowserMatch "MSIE" AuthDigestEnableQueryStringHack=On 


More info : http://httpd.apache.org/docs/2.0/mod/mod_auth_digest.html#msie 更多信息: http//httpd.apache.org/docs/2.0/mod/mod_auth_digest.html#msie

Then add this property in your code for the webrequest object: 然后在webrequest对象的代码中添加此属性:

    request.UserAgent = "MSIE"

it work very well for me 它对我很有用

I think the second URL points to dynamic page and you should first call it using GET to get the HTML and then to download it. 我认为第二个URL指向动态页面,您应首先使用GET调用它来获取HTML然后下载它。 No experience in this field though. 虽然没有这方面的经验。

In earlier answers everybody use the obsolete WEbREquest.Create method.在早期的答案中,每个人都使用过时的 WEbREquest.Create 方法。 So here is my async solution what up to date for recently trending's:所以这是我的异步解决方案,最新的趋势是什么:

public async Task<string> LoadHttpPageWithDigestAuthentication(string url, string username, string password)
    {
        Uri myUri = new Uri(url);
        NetworkCredential myNetworkCredential = new NetworkCredential(username, password);
        CredentialCache myCredentialCache = new CredentialCache { { myUri, "Digest", myNetworkCredential } };
        var request = new HttpClient(new HttpClientHandler() { Credentials = myCredentialCache, PreAuthenticate = true});

        var response = await request.GetAsync(url);

        var responseStream = await response.Content.ReadAsStreamAsync();

        StreamReader responseStreamReader = new StreamReader(responseStream, Encoding.Default);

        string answer = await responseStreamReader.ReadToEndAsync();

        return answer;
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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