0

我正在尝试访问 Lighttpd 服务器上的资源,该服务器强制完整的请求 URI 与授权请求标头中的 URI 匹配。这在RFC 7616中指定

认证服务器必须确保“uri”参数指定的资源与请求行中指定的资源相同;如果不是,服务器应该返回 400 Bad Request 错误。(由于这可能是攻击的征兆,服务器
实现者可能需要考虑记录此类错误。)
在此字段中从请求 URL 复制信息的目的是
处理中间代理可能更改
客户端请求的可能性-线。这个改变的(但可能在语义上
等价的)请求不会产生
与客户端计算的相同的摘要。

我正在使用Flurl 库(v1.4),它只是 HttpClient 的一个包装器。但是,HttpClientHandler 来自.Net。

为什么它使用基本 URI 而不是完整的 URI?它是一个错误吗?如何让它使用完整的 URI?

我想在管道中添加另一个HttpMessageHandler并使用完整的 URI 修改Authentication标头,但是HttpClientHandler不允许您设置InnerHandler

完整的Uri 应该是:http://base/resource.cgi?my=1¶ms=2

但这是请求标头中出现的内容:

授权: Digest username="user",realm="serve",nonce="5b911545:eaf4352d2113e5e4b1ca253bd70fd90a", uri="/base/resource.cgi" ,cnonce="bf7cf40f1289bc10bd07e8bf4784159c",nc=000,00001,qop="auth"响应="cf3c731ec93f7e5928f19f880f8325ab"

会导致 400 Bad Request响应。

我的 Flurl 代码:

HttpClient 工厂

    /// <summary>
    /// Custom factory to generate HttpClient and handler to use digest authentication and a continuous stream
    /// </summary>
    private class DigestHttpFactory : Flurl.Http.Configuration.DefaultHttpClientFactory
    {
        private CredentialCache CredCache { get; set; }

        public DigestHttpFactory(string url, string username, string password) : base()
        {
            Url = url;

            CredCache = new CredentialCache
            {
                { new Uri(Url), "Digest", new NetworkCredential(username, password) }
            };
        }

        private string Url { get; set; }

        public override HttpClient CreateHttpClient(HttpMessageHandler handler)
        {
            var client = base.CreateHttpClient(handler);
            client.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite); // To keep stream open indefinietly
            return client;
        }

        public override HttpMessageHandler CreateMessageHandler()
        {
            var handler = new HttpClientHandler
            {
                Credentials = CredCache.GetCredential(new Uri(Url), "Digest")
            };


            return handler;
        }
    }

请求代码

public class MyConnection
{
    public string BaseUrl => "http://base/resource.cgi";

    public async Task ConnectAsync(CancellationToken cancellationToken = default(CancellationToken))
    {
        ConnectionCancellation = new CancellationTokenSource();

        var url = BaseUrl
            .SetQueryParam("my", 1)
            .SetQueryParam("params", 2)

        FlurlHttp.ConfigureClient(url, client =>
        {
            client.Configure(settings =>
            {
                settings.HttpClientFactory = new DigestHttpFactory(url, Username, Password);
            });
        });

        try
        {
            using (var getResponse = url.GetAsync(cancellationToken, HttpCompletionOption.ResponseHeadersRead))
            {
                var responseMessage = await getResponse;

                using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(ConnectionCancellation.Token, cancellationToken))
                using (var stream = await responseMessage.Content.ReadAsStreamAsync())
                    await Process(stream, linkedCts.Token);
            }
        }
        catch (OperationCanceledException ex)
        {
            throw ex;
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }
}
4

1 回答 1

2

看来这确实是微软执行 RFC 的一个错误:

System.Net 类不包括摘要身份验证标头的“Uri”属性中的查询字符串。这违反了 RFC,并且一些 Web 服务器实现会拒绝这些请求。

该帖子详细说明了从这个答案中获取的解决方法。

于 2018-09-07T13:48:29.553 回答