WebClient генерирует (401) несанкционированную ошибку - PullRequest
32 голосов
/ 27 января 2010

У меня есть следующий код, работающий в службе Windows:

WebClient webClient = new WebClient();
webClient.Credentials = new NetworkCredential("me", "12345", "evilcorp.com");
webClient.DownloadFile(downloadUrl, filePath);

Каждый раз я получаю следующее исключение

{"The remote server returned an error: (401) Unauthorized."}

Со следующим внутренним исключением:

{"The function requested is not supported"}

Я точно знаю, что учетные данные действительны, на самом деле, если я захожу в downloadUrl в моем веб-браузере и вводю свои учетные данные как evilcorp.com \ me с паролем 12345, он загружается нормально.

Что странно, так это то, что если я укажу свои учетные данные как me@evilcorp.com с 12345, то, похоже, произойдет сбой.

Есть ли способ отформатировать учетные данные?

Ответы [ 5 ]

81 голосов
/ 13 сентября 2013

webClient.UseDefaultCredentials = true; решил мою проблему.

6 голосов
/ 28 января 2010

Очевидно, что ОС, с которой вы работаете, имеет значение, поскольку шифрование по умолчанию между ОС изменилось. Этот блог имеет больше деталей: http://ferozedaud.blogspot.com/2009/10/ntlm-auth-fails-with.html

Это, по-видимому, также обсуждалось при стекопереработке здесь: 407 Требуется аутентификация - вызов не отправлен

Я бы посоветовал прочитать блог в первую очередь, поскольку там есть очищенные знания.

2 голосов
/ 03 ноября 2018

Для меня 'webClient.UseDefaultCredentials = true;' решает его только на локальном, а не в веб-приложении на сервере, подключенном к другому серверу. Я не мог добавить необходимые учетные данные в Windows как пользователь, но позже я нашел какой-то способ программирования - я не буду проверять его, поскольку уже создал собственное решение. А также я не хочу манипулировать с реестром веб-сервера , даже если у меня есть необходимые права администратора. Все эти проблемы связаны с внутренней обработкой Windows аутентификации NTLM («Домен Windows») и всеми библиотеками и инфраструктурами, построенными на этом (например, .NET).

Таким образом, для меня решение было довольно простым - создать прокси-приложение в многоплатформенной технологии с многоплатформенной библиотекой NTLM, в которой связь NTLM создается вручную в соответствии с общедоступными спецификациями, а не путем запуска встроенного кода Окна. Я сам выбрал Node.js и библиотеку httpntlm , потому что она содержит только один единственный исходный файл с несколькими строками и вызывает его из .NET как программу, возвращающую загруженный файл (также я предпочитаю передавать ее через стандартный вывод вместо создания временного файла).

Программа Node.js в качестве прокси для загрузки файла за аутентификацией NTLM:

var httpntlm = require('httpntlm');         // https://github.com/SamDecrock/node-http-ntlm
//var fs = require('fs');

var login = 'User';
var password = 'Password';
var domain = 'Domain';

var file = process.argv.slice(2);           // file to download as a parameter

httpntlm.get({
    url: 'https://server/folder/proxypage.aspx?filename=' + file,
    username: login,
    password: password,
    workstation: '',
    domain: domain,
    binary: true                            // don't forget for binary files
}, function (err, res/*ponse*/) {
    if (err) { 
        console.log(err);
    } else {
        if (res.headers.location) {         // in my case, the server redirects to a similar URL,
            httpntlm.get({                  // now containing the session ID
                url: 'https://server' + res.headers.location,
                username: login,
                password: password,
                workstation: '',
                domain: domain,
                binary: true                // don't forget for binary files
            }, function (err, res) {
                if (err) { 
                    console.log(err);
                } else {
                      //console.log(res.headers);
                      /*fs.writeFile("434980.png", res.body, function (err) {  // test write
                          if (err)                                             // to binary file
                              return console.log("Error writing file");
                          console.log("434980.png saved");
                      });*/
                      console.log(res.body.toString('base64'));  // didn't find a way to output
                }                                                // binary file, toString('binary')
            });                                                  // is not enough (docs say it's
                                                                 // just 'latin1')...
        } else {           // if there's no redirect
            //console.log(res.headers);                          // ...so I output base64 and
            console.log(res.body.toString('base64'));            // convert it back in the caller 
        }                                                        // code
    }
});

Код вызывающего абонента .NET (веб-приложение, загружающее файлы из веб-приложения на другой сервер)

public static string ReadAllText(string path)
{
    if (path.StartsWith("http"))
        return System.Text.Encoding.Default.GetString(ReadAllBytes(path));
    else
        return System.IO.File.ReadAllText(path);
}

public static byte[] ReadAllBytes(string path)
{
    if (path.StartsWith("http"))
    {
        ProcessStartInfo psi = new ProcessStartInfo();
        psi.FileName = "node.exe";                     // Node.js installs into the PATH
        psi.Arguments = "MyProxyDownladProgram.js " + 
            path.Replace("the base URL before the file name", "");
        psi.WorkingDirectory = "C:\\Folder\\With My\\Proxy Download Program";
        psi.UseShellExecute = false;
        psi.CreateNoWindow = true;
        psi.RedirectStandardInput = true;
        psi.RedirectStandardOutput = true;
        psi.RedirectStandardError = true;
        Process p = Process.Start(psi);
        byte[] output;
        try
        {
            byte[] buffer = new byte[65536];
            using (var ms = new MemoryStream())
            {
                while (true)
                {
                    int read = p.StandardOutput.BaseStream.Read(buffer, 0, buffer.Length);
                    if (read <= 0)
                        break;
                    ms.Write(buffer, 0, read);
                }
                output = ms.ToArray();
            }
            p.StandardOutput.Close();
            p.WaitForExit(60 * 60 * 1000);             // wait up to 60 minutes 
            if (p.ExitCode != 0)
                throw new Exception("Exit code: " + p.ExitCode);
        }
        finally
        {
            p.Close();
            p.Dispose();
        }
        // convert the outputted base64-encoded string to binary data
        return System.Convert.FromBase64String(System.Text.Encoding.Default.GetString(output));
    }
    else
    {
        return System.IO.File.ReadAllBytes(path);
    }
}
2 голосов
/ 28 января 2010

Согласно документам msdn исключение может быть вызвано тем, что метод был вызван одновременно в нескольких потоках. Для метода DownloadFile также требуется полный URL-адрес, такой как http://evilcorp.com/.

0 голосов
/ 20 августа 2015

Во время написания URL, ставьте '@' перед строкой URL.

Например:

var url = @"http://evilcorp.com";
WebClient webClient = new WebClient();
webClient.Proxy = null;
webClient.DownloadFile(new Uri(url), filePath);

Это решит вашу проблему.

Ответ, данный П. Брайаном. Маки, тоже правильный.

...