web-dev-qa-db-ja.com

WebClientが生成する(401)不正なエラー

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"}

資格情報が有効であることは確かです。実際、WebブラウザーでdownloadUrlにアクセスし、パスワード12345でevilcorp.com\meとして資格情報を入力すると、正常にダウンロードされます。

奇妙なのは、資格情報を[email protected]に12345で指定すると、失敗するように見えることです。

資格情報をフォーマットする方法はありますか?

32
Matt

デフォルトの暗号化はOS間で変更されているため、実行しているOSが重要なようです。このブログには詳細があります: http://ferozedaud.blogspot.com/2009/10/ntlm-auth-fails-with.html

これは明らかにスタックオーバーフローについてもここで議論されています: 407認証が必要です-チャレンジは送信されませんでした

蒸留された知識があるので、まずブログを読むことをお勧めします。

6
Aryabhatta

webClient.UseDefaultCredentials = true;は私の問題を解決しました。

82
P.Brian.Mackey

私にとっては、「webClient.UseDefaultCredentials = true;」別のサーバーに接続しているサーバー上のWebアプリではなく、ローカルでのみ解決します。ユーザーとしてWindowsに必要な資格情報を追加できませんでしたが、後で何らかのプログラミング方法を見つけました-既に独自のソリューションを作成しているため、テストしません。また、必要な管理者権限を持っている場合でも、 Webサーバーのレジストリを変更する はしたくない。これらの問題はすべて、NTLM認証のWindows内部処理(「Windowsドメイン」)と、その上に構築されたすべてのライブラリとフレームワーク(.NETなど)によるものです。

したがって、私にとっての解決策は非常に簡単でした-Windowsで組み込みコードを実行するのではなく、公共仕様に従ってNTLM通信が手動で作成されるマルチプラットフォームNTLMライブラリを使用して、マルチプラットフォームテクノロジでプロキシアプリを作成します。 Node.jsと httpntlmライブラリ を選択したのは、数行のソースファイルが1つだけで、ダウンロードしたファイルを返すプログラムとして.NETから呼び出すためです(また、一時ファイルを作成する代わりに標準出力)。

NTLM認証の背後にあるファイルをダウンロードするプロキシとしてのNode.jsプログラム:

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呼び出し元コード(別のサーバー上のWebアプリからファイルをダウンロードするWebアプリ)

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
Ladislav Zima

msdn docs によると、例外は、メソッドが複数のスレッドで同時に呼び出されたためである可能性があります。 DownloadFileメソッドには、http://evilcorp.com/ などの完全修飾URLも必要です=

2
jac