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で指定すると、失敗するように見えることです。
資格情報をフォーマットする方法はありますか?
デフォルトの暗号化はOS間で変更されているため、実行しているOSが重要なようです。このブログには詳細があります: http://ferozedaud.blogspot.com/2009/10/ntlm-auth-fails-with.html
これは明らかにスタックオーバーフローについてもここで議論されています: 407認証が必要です-チャレンジは送信されませんでした
蒸留された知識があるので、まずブログを読むことをお勧めします。
webClient.UseDefaultCredentials = true;
は私の問題を解決しました。
私にとっては、「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);
}
}
msdn docs によると、例外は、メソッドが複数のスレッドで同時に呼び出されたためである可能性があります。 DownloadFileメソッドには、http://evilcorp.com/ などの完全修飾URLも必要です=。