【问题标题】:HttpListener Access DeniedHttpListener 访问被拒绝
【发布时间】:2026-02-20 10:50:02
【问题描述】:

我正在用 C# 编写一个 HTTP 服务器。

当我尝试执行函数HttpListener.Start() 时,我得到一个HttpListenerException

“拒绝访问”。

当我在 Windows 7 中以管理员模式运行该应用程序时,它运行良好。

我可以让它在没有管理员模式的情况下运行吗?如果是的话怎么办? 如果不是,如何在开始运行后将应用更改为管理员模式?

using System;
using System.Net;

namespace ConsoleApplication1
{
    class Program
    {
        private HttpListener httpListener = null;

        static void Main(string[] args)
        {
            Program p = new Program();
            p.Server();
        }

        public void Server()
        {
            this.httpListener = new HttpListener();

            if (httpListener.IsListening)
                throw new InvalidOperationException("Server is currently running.");

            httpListener.Prefixes.Clear();
            httpListener.Prefixes.Add("http://*:4444/");

            try
            {
                httpListener.Start(); //Throws Exception
            }
            catch (HttpListenerException ex)
            {
                if (ex.Message.Contains("Access is denied"))
                {
                    return;
                }
                else
                {
                    throw;
                }
            }
        }
    }
}

【问题讨论】:

  • 如果有人想避免这个错误,他可以尝试用 TcpListener 编写它。它不需要管理员权限
  • 我面临同样的问题,在 Visual Studio 2008 + Windows 7 中,它会产生“拒绝访问”错误,解决这个问题的方法是在管理员模式下运行 Visual Studio 2008

标签: windows-7 c#-4.0 uac httplistener


【解决方案1】:

是的,您可以在非管理员模式下运行 HttpListener。您需要做的就是授予特定 URL 的权限。例如

netsh http add urlacl url=http://+:80/MyUri user=DOMAIN\user

文档是here

【讨论】:

  • 这很有帮助,但为了完整起见,这行代码中指定的 URL:httpListener.Prefixes.Add("http://*:4444/"); 必须与 netsh 命令中的 URL 完全匹配。例如,我有httpListener.Prefixes.Add("http://127.0.0.1:80/"); 和你有相同的netsh 命令,但仍然会抛出 HttpListenerException。我需要更改 httpListener.Prefixes.Add("http://+:80/");感谢您的帮助 @Darrel Miller,因为您让我走上了解决这个问题的正确道路!
  • 如果“MyUri”为空,请不要忘记尾部斜杠,否则您将收到The parameter is incorrect 错误。示例:url=http://+:80/
  • 对于非管理员用户,即使http://localhost:80/ 有什么方法可以做到这一点?我有一个桌面应用程序需要在这样的 URL 上接收一个请求,而要求管理员将其安装在 50 个桌面上似乎是一种耻辱,只是为了这个目的。
  • 显然不是。文档页面中也没有提到需要提升或管理权限。因此很容易假设这将充当“更智能”的 TCPListener,而实际上有主要理由不使用它 - 但这没有记录。
  • 运行 netsh 需要管理员 ;(
【解决方案2】:

我可以让它在没有管理员模式的情况下运行吗?如果是的话怎么办?如果不是,如何在开始运行后将应用更改为管理员模式?

你不能,它必须从提升的权限开始。您可以使用runas 动词重新启动它,这将提示用户切换到管理员模式

static void RestartAsAdmin()
{
    var startInfo = new ProcessStartInfo("yourApp.exe") { Verb = "runas" };
    Process.Start(startInfo);
    Environment.Exit(0);
}

编辑:实际上,这不是真的; HttpListener 可以在没有提升权限的情况下运行,但您需要为要侦听的 URL 授予权限。详情请见Darrel Miller's answer

【讨论】:

  • 你能解释一下为什么我必须从提升的权限开始吗?
  • 你还需要添加这一行'startInfo.UseShellExecute = false;'在'Process.Start(startInfo);'之前
  • @Randall:因为这就是 Windows 的工作方式……进程在运行时无法切换到管理员模式。关于 UseShellExecute:这取决于你正在执行什么。我用“notepad.exe”测试了我的代码,没有 UseShellExecute = false 它可以正常工作
  • 谢谢。关于 UseShellExecute:我试图运行我发布的代码。另一个问题是,由于某种原因,它曾经询问我是否想以管理员身份运行,之后的任何其他时间它都没有询问。我重新启动,调试以确保它去那里并且什么都没有。有什么建议吗?
  • @Thomas 在非管理员模式下运行 HttpListener 没有问题。
【解决方案3】:

如果您使用http://localhost:80/ 作为前缀,则无需管理员权限即可侦听http 请求。

【讨论】:

  • 你能发布一些示例代码吗?我刚刚用http://localhost:80/ 尝试了这个并得到了“拒绝访问”。
  • 抱歉,这似乎不起作用。请检查您是否以管理员身份运行,这不应该是正常情况。
  • 如果这可行,我会认为这是一个 Windows 错误。无论您如何看待 Windows,我都认为这是一个非常基本的水平,他们很可能不会错过正确处理。
  • 所以 2 年多后,这对我现在适用于带有 .NET 框架 4.5 的 Windows Server 2008 R2。 httpListener.Prefixes.Add("http://*:4444/"); 确实显示 Access Denied 错误,但 httpListener.Prefixes.Add("http://localhost:4444/"); 工作没有任何问题。微软似乎从这些限制中排除了localhost。有趣的是,httpListener.Prefixes.Add("http://127.0.0.1:4444/"); 仍然显示拒绝访问错误,所以唯一有效的是localhost:{any port}
  • @Tony 谢谢你的评论实际上对我来说是最有价值的。我在几个配置中有“127.0.0.1”。 QA 报告说它在 Windows 8.1 上无法运行,但在 Windows 10 中运行良好(我自己在 Win10 中对其进行了测试)。奇怪的是,微软似乎允许所有人在 Win10 中使用 127.0.0.1 而不是 8.1(可能是以前的版本),但果然,localhost 没问题。谢谢
【解决方案4】:

语法对我来说是错误的,你必须包括引号:

netsh http add urlacl url="http://+:4200/" user=everyone

否则我会收到“参数不正确”

【讨论】:

  • "参数不正确"如果不指定url后面的/也可以返回
【解决方案5】:

作为不需要提升或 netsh 的替代方案,您也可以使用 TcpListener 例如。

以下是此示例的修改摘录: https://github.com/googlesamples/oauth-apps-for-windows/tree/master/OAuthDesktopApp

// Generates state and PKCE values.
string state = randomDataBase64url(32);
string code_verifier = randomDataBase64url(32);
string code_challenge = base64urlencodeNoPadding(sha256(code_verifier));
const string code_challenge_method = "S256";

// Creates a redirect URI using an available port on the loopback address.
var listener = new TcpListener(IPAddress.Loopback, 0);
listener.Start();
string redirectURI = string.Format("http://{0}:{1}/", IPAddress.Loopback, ((IPEndPoint)listener.LocalEndpoint).Port);
output("redirect URI: " + redirectURI);

// Creates the OAuth 2.0 authorization request.
string authorizationRequest = string.Format("{0}?response_type=code&scope=openid%20profile&redirect_uri={1}&client_id={2}&state={3}&code_challenge={4}&code_challenge_method={5}",
    authorizationEndpoint,
    System.Uri.EscapeDataString(redirectURI),
    clientID,
    state,
    code_challenge,
    code_challenge_method);

// Opens request in the browser.
System.Diagnostics.Process.Start(authorizationRequest);

// Waits for the OAuth authorization response.
var client = await listener.AcceptTcpClientAsync();

// Read response.
var response = ReadString(client);

// Brings this app back to the foreground.
this.Activate();

// Sends an HTTP response to the browser.
WriteStringAsync(client, "<html><head><meta http-equiv='refresh' content='10;url=https://google.com'></head><body>Please close this window and return to the app.</body></html>").ContinueWith(t =>
{
    client.Dispose();
    listener.Stop();

    Console.WriteLine("HTTP server stopped.");
});

// TODO: Check the response here to get the authorization code and verify the code challenge

读写方法为:

private string ReadString(TcpClient client)
{
    var readBuffer = new byte[client.ReceiveBufferSize];
    string fullServerReply = null;

    using (var inStream = new MemoryStream())
    {
        var stream = client.GetStream();

        while (stream.DataAvailable)
        {
            var numberOfBytesRead = stream.Read(readBuffer, 0, readBuffer.Length);
            if (numberOfBytesRead <= 0)
                break;

            inStream.Write(readBuffer, 0, numberOfBytesRead);
        }

        fullServerReply = Encoding.UTF8.GetString(inStream.ToArray());
    }

    return fullServerReply;
}

private Task WriteStringAsync(TcpClient client, string str)
{
    return Task.Run(() =>
    {
        using (var writer = new StreamWriter(client.GetStream(), new UTF8Encoding(false)))
        {
            writer.Write("HTTP/1.0 200 OK");
            writer.Write(Environment.NewLine);
            writer.Write("Content-Type: text/html; charset=UTF-8");
            writer.Write(Environment.NewLine);
            writer.Write("Content-Length: " + str.Length);
            writer.Write(Environment.NewLine);
            writer.Write(Environment.NewLine);
            writer.Write(str);
        }
    });
}

【讨论】:

  • 这非常有用,因为我们在为 IdentityModel.OidcClient 库实现 IBrowser 时遇到了 HttpListener 问题,因此与上面的用例非常相似。
  • 值得注意的是,上面的代码虽然有错误。首先在某些浏览器中使用带有 UTF8 的 StreamWriter 会导致问题,我猜是因为正在输出 BOM 或类似的东西。我把它改成了ASCII,一切都很好。我还添加了一些代码来等待数据可以在流上读取,因为它有时不会返回任何数据。
  • 谢谢@mackie - 这部分实际上是直接取自微软自己的样本。我猜他们并没有真正正确地测试它。如果您可以编辑我的答案,请继续并修复方法 - 否则可能会将更改发送给我,我可以更新它。
  • 应该使用以下构造函数 new UTF8Encoding(false),而不是使用 ASCII,它会禁用 BOM 的发射。我已经在答案中更改了这个
  • 我更喜欢这个解决方案而不是接受的答案。运行 netsh 似乎是个 hack。这是一个可靠的程序化解决方案。谢谢!
【解决方案6】:

如果您想使用标志“user=Everyone”,您需要将其调整为您的系统语言。如前所述:

netsh http add urlacl url=http://+:80/ user=Everyone

德语应该是:

netsh http add urlacl url=http://+:80/ user=Jeder

【讨论】:

  • 在西班牙语系统中,执行:user=Todos
  • 另外,我需要将 URL 放在引号中,如另一个答案中所述。
  • 波兰语用户=wszyscy
【解决方案7】:

默认情况下,Windows 定义了以下所有人都可以使用的前缀: http://+:80/Temporary_Listen_Addresses/

所以您可以通过以下方式注册您的HttpListener

Prefixes.Add("http://+:80/Temporary_Listen_Addresses/" + Guid.NewGuid().ToString("D") + "/";

这有时会导致 Skype 等默认尝试使用端口 80 的软件出现问题。

【讨论】:

  • Skype 是罪魁祸首。我将端口更改为 not 为 80 并且它工作正常。
【解决方案8】:

如果您将应用程序清单添加到您的项目中,您可以以管理员身份启动您的应用程序。

只需将新项目添加到您的项目并选择“应用程序清单文件”。将&lt;requestedExecutionLevel&gt; 元素更改为:

<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />

【讨论】:

    【解决方案9】:
    httpListener.Prefixes.Add("http://*:4444/");
    

    您使用“*”,因此您以管理员身份执行以下 cmd

    netsh http add urlacl url=http://*:4444/ user=username
    

    不使用+,必须使用*,因为你指定*:4444~。

    https://msdn.microsoft.com/en-us/library/system.net.httplistener.aspx

    【讨论】:

    • 这是给我的。 .Add("http://+:9000/") 对我有用
    【解决方案10】:

    我也遇到过类似的问题。如果您已经保留了 url,那么您必须先删除该 url 才能在非管理员模式下运行,否则它将失败并显示 Access is Denied 错误。

    netsh http delete urlacl url=http://+:80
    

    【讨论】:

      【解决方案11】:

      谢谢大家, 这很有帮助。 只是为了添加更多 [来自 MS 页面]:

      警告

      不应使用*通配符绑定(http://*:8080/http://+:8080)。*通配符绑定可能会使您的应用程序面临安全漏洞。这适用于强通配符和弱通配符。使用明确的主机名而不是通配符。如果您控制整个父域(相对于易受攻击的*.com),子域通配符绑定(例如,*.mysub.com)就不存在这种安全风险。有关详细信息,请参阅 rfc7230 第 5.4 节。

      【讨论】:

        【解决方案12】:

        不幸的是,由于某些可能与 HTTPS 和证书相关的原因,本机 .NET HttpListener 需要管理员权限,甚至对于仅 HTTP 协议...

        优点

        有趣的是,HTTP 协议位于 TCP 协议之上,但启动 C# TCP 侦听器不需要任何管理员权限即可运行。换句话说,在概念上可以实现不需要管理员权限的 HTTP 服务器。

        替代方案

        下面是一个不需要管理员权限的项目示例: https://github.com/EmilianoElMariachi/ElMariachi.Http.Server

        【讨论】:

          最近更新 更多