Как обрабатывать аутентификацию с помощью HttpWebRequest.AllowAutoRedirect?

В соответствии с MSDN, когда свойство HttpWebRequest.AllowAutoRedirect равно true, перенаправления очистят заголовки аутентификации. Обходным путем является реализация IAuthenticationModule для проверки подлинности:

Заголовок авторизации очищается при автопереадресации, и HttpWebRequest автоматически пытается повторно аутентифицироваться в перенаправленном местоположении. На практике это означает, что приложение не может помещать пользовательскую информацию аутентификации в заголовок авторизации, если можно встретить перенаправление. Вместо этого приложение должно реализовать и зарегистрировать собственный модуль аутентификации. System.Net.AuthenticationManager и связанный с ним класс используются для реализации настраиваемого модуля аутентификации. Метод AuthenticationManager.Register регистрирует настраиваемый модуль аутентификации.

Я создал базовую реализацию этого интерфейса:

public class CustomBasic : IAuthenticationModule
{
    public CustomBasic() { }

    public string AuthenticationType { get { return "Basic"; } }

    public bool CanPreAuthenticate { get { return true; } }

    private bool checkChallenge(string challenge, string domain)
    {
        if (challenge.IndexOf("Basic", StringComparison.InvariantCultureIgnoreCase) == -1) { return false; }
        if (!string.IsNullOrEmpty(domain) && challenge.IndexOf(domain, StringComparison.InvariantCultureIgnoreCase) == -1) { return false; }
        return true;
    }

    public Authorization PreAuthenticate(WebRequest request, ICredentials credentials)
    {
        return authenticate(request, credentials);
    }

    public Authorization Authenticate(String challenge, WebRequest request, ICredentials credentials)
    {
        if (!checkChallenge(challenge, string.Empty)) { return null; }
        return this.authenticate(request, credentials);
    }

    private Authorization authenticate(WebRequest webRequest, ICredentials credentials)
    {
        NetworkCredential requestCredentials = credentials.GetCredential(webRequest.RequestUri, this.AuthenticationType);
        return (new Authorization(string.Format("{0} {1}", this.AuthenticationType, Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Format("{0}:{1}", requestCredentials.UserName, requestCredentials.Password))))));
    }
}

и простой драйвер для реализации функциональных возможностей:

public class Program
{
    static void Main(string[] args)
    {
        // replaces the existing handler for Basic authentication
        AuthenticationManager.Register(new CustomBasic());
        // make a request that requires authentication
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(@"https://www.SomeUrlThatRequiresAuthentication.com");
        request.Method = "GET";
        request.KeepAlive = false;
        request.ContentType = "text/plain";
        request.AllowAutoRedirect = true;
        request.Credentials = new NetworkCredential("userName", "password");
        HttpWebResponse result = (HttpWebResponse)request.GetResponse();
    }
}

Когда я делаю запрос, который не перенаправляет, вызывается метод Authenticate в моем классе, и аутентификация выполняется успешно. Когда я делаю запрос, который повторяет ответ 307 (временный перенаправление), методы моего класса не вызываются, и аутентификация завершается с ошибкой. Что здесь происходит?

Я бы предпочел не отключать автоматическую переадресацию и писать собственную логику для обработки ответов 3xx. Как я могу заставить свою логику аутентификации работать с автопереадресацией?

Ответ 1

Вместо NetworkCredential вы должны передать CredentialCache для request.Credentials.

CredentialCache cache = new CredentialCache();
cache.Add(new Uri(@"https://www.SomeUrlThatRequiresAuthentication.com", "Basic", new NetworkCredential("username", "password"));
request.Credentials = cache;

В соответствии с документацией MSDN:

Класс CredentialCache хранит учетные данные для нескольких Интернет Ресурсы. Приложениям, которым требуется доступ к нескольким ресурсам, могут хранить учетные данные для этих ресурсов в CredentialCache экземпляр, который затем предоставляет правильный набор учетных данных для Интернет-ресурс, если требуется. Когда метод GetCredential он сравнивает унифицированный идентификатор ресурса (URI) и тип аутентификации, предоставляемый с теми, которые хранятся в кеше и возвращает первый набор учетных данных, которые соответствуют.

Ответ 2

Надеюсь, следующий следующий вариант, который я взял из кода проекта url http://www.codeproject.com/Articles/49243/Handling-Cookies-with-Redirects-and-HttpWebRequest

    String targetUrl = "https://www.SomeUrlThatRequiresAuthentication.com";

    HttpWebRequest request = GetNewRequest(targetUrl);
    HttpWebResponse response = (HttpWebResponse)request.GetResponse();

    while (response.StatusCode ==  HttpStatusCode.MovedPermanently)
    {
        response.Close();
        request = GetNewRequest(response.Headers["Location"]);
        response = (HttpWebResponse)request.GetResponse();
    }


private static HttpWebRequest GetNewRequest(string targetUrl)
{

    HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(targetUrl);
    request.AllowAutoRedirect = false;
    request.Headers.Add("Authorization", "Basic xxxxxxxx");
    return request;
}

Ответ 3

Несмотря на то, что ОП очень старый, я бы назначил ответ Кратера как ОТВЕТ. Я прошел аналогичные функции, включая создание настраиваемого модуля аутентификации, хотя веб-ресурс, к которому я обращался, использовался только для обычной проверки подлинности. Я обнаружил, что только после того, как я использовал CredentialCache вместо простого NetworkCredential, мой модуль аутентификации вызывался после перенаправления.

Кроме того, я обнаружил, что, поскольку для проверки подлинности мне нужен был Basic, просто поставляя CredentialCache, мне не нужен собственный модуль аутентификации - стандартный базовый модуль работал нормально.

Следующий ресурс, похоже, подтвердил это (в отличие от ссылки документации .NET, упомянутой в OP):

https://blogs.msdn.microsoft.com/ncl/2009/05/05/custom-http-authentication-schemes/

Ответ 4

Что вам нужно сделать, скорее всего, запрос POST. Вы отправляете переменные для аутентификации, поэтому вам нужно использовать действие POST.

Смотрите это сообщение для получения дополнительной информации: Войдите на сайт, используя С#

Автоматическое перенаправление не должно мешать этому сообщению. Я бы рекомендовал установить Fiddler и войти в систему вручную, а затем посмотреть, что происходит.

* Имейте в виду, что при отправке запроса POST, если есть форма входа, вы отправляете запрос POST тегу action='/some-url-or-whatever.php' в форме. POST данные к этому, и вы должны иметь возможность войти в систему просто отлично.

Сообщите мне, если это поможет.