我在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"}
我肯定知道凭据是有效的,事实上,如果我在我的网络浏览器中下载urrl并输入我的凭据evilcorp.com \ me,密码为12345,则下载正常。
但奇怪的是,如果我将我的凭证指定为me@evilcorp.com 12345,它似乎会失败。
有没有办法格式化凭据?
答案 0 :(得分:81)
webClient.UseDefaultCredentials = true;
解决了我的问题。
答案 1 :(得分:6)
显然,您运行的操作系统很重要,因为默认加密在操作系统之间发生了变化。 此博客包含更多详细信息:http://ferozedaud.blogspot.com/2009/10/ntlm-auth-fails-with.html
这显然也在stackoverflow上进行了讨论:407 Authentication required - no challenge sent
我建议先阅读博客,因为那里有蒸馏知识。
答案 2 :(得分:2)
根据msdn docs,异常可能是因为该方法已在多个线程上同时调用。 DownloadFile 方法还需要完全限定的网址,例如 http://evilcorp.com/ 。
答案 3 :(得分:2)
对我来说,“ webClient.UseDefaultCredentials = true;”仅在本地解决此问题,而不是在连接到另一台服务器的服务器上的Web应用程序中解决。我无法以用户身份将所需的凭据添加到Windows中,但是后来我发现了一些编程方式-因为我已经制定了自己的解决方案,所以我不会对其进行测试。而且,即使我具有所需的管理员权限,我也不想mangle with the web server's registry。所有这些问题是由于Windows内部对NTLM身份验证(“ Windows域”)进行处理,以及由于该问题而构建的所有库和框架(例如.NET)的原因。
因此,对我而言,解决方案的想法非常简单-使用具有多平台NTLM库的多平台技术创建代理应用,其中NTLM通信是根据公共规范手动创建的,而不是通过运行内置代码视窗。我自己选择了Node.js和the httpntlm library,因为它仅是一个单一的源文件,只有几行,并作为返回下载文件的程序从.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调用者代码(该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);
}
}
答案 4 :(得分:0)
嗯。很多答案,但我想知道回答您的最后一个问题是否能解决所有问题。 “我”不是授权类型(当然,除非您的服务器已添加对它的支持!)。您可能需要“基本”。
还请记住,某些Web服务需要您在初始请求上发送授权标头,而这样做不会这样做。相反,它会在从服务器获取授权要求的响应后做出响应。如果需要,则需要创建自己的Authorization标头。
String basicToken = Base64Encoding.EncodeStringToBase64(String.Format("{0}:{1}", clientId.Trim(), clientSecret.Trim()));
webClient.Headers.Add("Authorization", String.Format("Basic {0}", basicToken));
当然,正如人们指出的那样,如果您在Windows环境中使用IIS(或其他具有Windows安全意识的HTTP服务器),请将UseDefaultCredentials设置为true即可。
答案 5 :(得分:-2)
在编写URL时,将“@”放在url字符串前面。
例如:
var url = @"http://evilcorp.com";
WebClient webClient = new WebClient();
webClient.Proxy = null;
webClient.DownloadFile(new Uri(url), filePath);
这将解决您的问题。
P.Brian.Mackey给出的答案也是正确的。