5

私のコードは、基本認証を必要とする Web サービス URL への HTTP GET を作成しています。

プロパティが定義されているを使用HttpClientしてこれを実装しました。HttpClientHandlerCredentials

これはすべて完全に機能します..認証された GET を作成しているユースケースの 1 つを除いて: http://somedomain.comにリダイレクトしhttp://www.somedomain.comます。

リダイレクト中に HttpClientHandler が認証ヘッダーをクリアしているようです。どうすればこれを防ぐことができますか? リダイレクトに関係なく資格情報を送信したい。

これは私のコードです:

// prepare the request
var request = new HttpRequestMessage(method, url);
using (var handler = new HttpClientHandler { Credentials = new NetworkCredential(username, password) , PreAuthenticate = true })
using (var client = new HttpClient(handler))
{
    // send the request
    var response = await client.SendAsync(request);

注: これは関連する質問です: リダイレクト中に HTTP 基本認証を有効に保つ しかし、リクエストを行うために異なるクラスを使用しているため、より適切で具体的な解決策があるかもしれません

4

2 に答える 2

3

デフォルトの HttpClientHandler は、内部で同じ HttpWebRequest インフラストラクチャを使用します。NetworkCredential を Credentials プロパティに割り当てる代わりに、CredentialCache を作成して割り当てます。

これは私が AutoRedirect の代わりに使用するものであり、async/await の妖精の粉を少し使用すると、おそらくはるかにきれいで信頼性が高くなります。

 public class GlobalRedirectHandler : DelegatingHandler {

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
        var tcs = new TaskCompletionSource<HttpResponseMessage>();

        base.SendAsync(request, cancellationToken)
            .ContinueWith(t => {
                HttpResponseMessage response;
                try {
                    response = t.Result;
                }
                catch (Exception e) {
                    response = new HttpResponseMessage(HttpStatusCode.ServiceUnavailable);
                    response.ReasonPhrase = e.Message;
                }
                if (response.StatusCode == HttpStatusCode.MovedPermanently
                    || response.StatusCode == HttpStatusCode.Moved
                    || response.StatusCode == HttpStatusCode.Redirect
                    || response.StatusCode == HttpStatusCode.Found
                    || response.StatusCode == HttpStatusCode.SeeOther
                    || response.StatusCode == HttpStatusCode.RedirectKeepVerb
                    || response.StatusCode == HttpStatusCode.TemporaryRedirect

                    || (int)response.StatusCode == 308) 
                {

                    var newRequest = CopyRequest(response.RequestMessage);

                    if (response.StatusCode == HttpStatusCode.Redirect 
                        || response.StatusCode == HttpStatusCode.Found
                        || response.StatusCode == HttpStatusCode.SeeOther)
                    {
                        newRequest.Content = null;
                        newRequest.Method = HttpMethod.Get;

                    }
                    newRequest.RequestUri = response.Headers.Location;

                    base.SendAsync(newRequest, cancellationToken)
                        .ContinueWith(t2 => tcs.SetResult(t2.Result));
                }
                else {
                    tcs.SetResult(response);
                }
            });

        return tcs.Task;
    }

    private static HttpRequestMessage CopyRequest(HttpRequestMessage oldRequest) {
        var newrequest = new HttpRequestMessage(oldRequest.Method, oldRequest.RequestUri);

        foreach (var header in oldRequest.Headers) {
            newrequest.Headers.TryAddWithoutValidation(header.Key, header.Value);
        }
        foreach (var property in oldRequest.Properties) {
            newrequest.Properties.Add(property);
        }
        if (oldRequest.Content != null) newrequest.Content = new StreamContent(oldRequest.Content.ReadAsStreamAsync().Result);
        return newrequest;
    }
}
于 2013-10-21T11:29:31.003 に答える
0

@DarrelMiller のソリューションを使用しましたが、機能します。ただし、いくつかの改善を行いました

私はコードをリファクタリングしたので、すべてが引数としてCopyRequest取られるようになりました。response

var newRequest = CopyRequest(response);

base.SendAsync(newRequest, cancellationToken)
    .ContinueWith(t2 => tcs.SetResult(t2.Result));

これは私が改善した CopyRequest メソッドです

  • 新しいコンテンツを作成しStreamContentて null に設定する代わりにRedirect / Found / SeeOther、コンテンツは必要な場合にのみ設定されます。
  • RequestUri は、Location が設定されている場合にのみ設定され、相対 URI ではない可能性があることを考慮します。
  • 最も重要なこと: 新しい Uri を確認し、ホストが一致しない場合は、認証情報が外部ホストに漏洩するのを防ぐために、認証ヘッダーをコピーしません。
private static HttpRequestMessage CopyRequest(HttpResponseMessage response)
{
    var oldRequest = response.RequestMessage;

    var newRequest = new HttpRequestMessage(oldRequest.Method, oldRequest.RequestUri);

    if (response.Headers.Location != null)
    {
        if (response.Headers.Location.IsAbsoluteUri)
        {
            newRequest.RequestUri = response.Headers.Location;
        }
        else
        {
            newRequest.RequestUri = new Uri(newRequest.RequestUri, response.Headers.Location);
        }
    }

    foreach (var header in oldRequest.Headers)
    {
        if (header.Key.Equals("Authorization", StringComparison.OrdinalIgnoreCase) && !(oldRequest.RequestUri.Host.Equals(newRequest.RequestUri.Host)))
        {
            //do not leak Authorization Header to other hosts
            continue;
        }
        newRequest.Headers.TryAddWithoutValidation(header.Key, header.Value);
    }

    foreach (var property in oldRequest.Properties)
    {
        newRequest.Properties.Add(property);
    }

    if (response.StatusCode == HttpStatusCode.Redirect
        || response.StatusCode == HttpStatusCode.Found
        || response.StatusCode == HttpStatusCode.SeeOther)
    {
        newRequest.Content = null;
        newRequest.Method = HttpMethod.Get;
    }
    else if (oldRequest.Content != null)
    {
        newRequest.Content = new StreamContent(oldRequest.Content.ReadAsStreamAsync().Result);
    }

    return newRequest;
}
于 2020-02-18T09:25:47.067 に答える