【问题标题】:WebClient generates (401) Unauthorized errorWebClient 生成 (401) 未经授权的错误
【发布时间】:2011-01-10 03:30:29
【问题描述】:

我在 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,它似乎失败了。

有没有办法格式化凭据?

【问题讨论】:

  • 你有没有为这个问题想出解决方案(代码)?

标签: c# webclient download


【解决方案1】:

嗯。很多答案,但我想知道回答你的最后一个问题是否会解决所有问题。 “我”不是授权类型(当然,除非您的服务器添加了对它的支持!)。您可能想要“基本”。

另外请记住,某些 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 有效。

【讨论】:

    【解决方案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 调用者代码(网络应用从另一台服务器上的网络应用下载文件)

    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);
        }
    }
    

    【讨论】:

      【解决方案3】:

      webClient.UseDefaultCredentials = true; 解决了我的问题。

      【讨论】:

      • 大多数情况下需要这个设置是服务器需要获取用户的身份信息,比如ASP.Net网站配置Windows认证。
      • Brian 如果你再靠近一点,我发誓我现在就吻你!!与这个斗争了几个小时,只错过了这个设置!? :-o 谢谢!!
      • 当然也帮助了我。谢谢布赖恩
      • 为什么会这样?这是否意味着 WebClient 将使用运行该应用程序的任何人的凭据?这个解决方案对我有用,但我正在为其他人编写这个控制台应用程序,我希望它在另一个用户运行它时工作,即使他自己可能没有正确的权限。
      【解决方案4】:

      根据msdn docs,异常可能是因为该方法已在多个线程上同时调用。 DownloadFile 方法还需要一个完全限定的 URL,例如 http://evilcorp.com/

      【讨论】:

        【解决方案5】:

        显然,您正在运行的操作系统很重要,因为默认加密在操作系统之间发生了变化。 此博客有更多详细信息:http://ferozedaud.blogspot.com/2009/10/ntlm-auth-fails-with.html

        这显然也在 stackoverflow 上讨论过:407 Authentication required - no challenge sent

        我建议先阅读博客,因为那里有提炼的知识。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-04-19
          • 1970-01-01
          • 1970-01-01
          • 2017-12-20
          • 1970-01-01
          • 2015-10-14
          • 1970-01-01
          相关资源
          最近更新 更多